From b0e940b806df297f4b93ace19bddabbee2b5c079 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Fri, 22 Feb 2019 17:25:42 +0100 Subject: [PATCH 01/14] added Bichinger Software & Consulting to acknowledgements for Redactor 3 version upgrade --- README.md | 4 ++++ doc/bichinger_logo.png | Bin 0 -> 2702 bytes 2 files changed, 4 insertions(+) create mode 100644 doc/bichinger_logo.png diff --git a/README.md b/README.md index d42a72e31..b833320ee 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,10 @@ Twitter: [@GroceryBagHead](https://twitter.com/grocerybaghead) Thanks to [Roman Almeida](https://github.com/nasmorn) for contributing OEM License for [Redactor Text Editor](http://imperavi.com/redactor) +Thanks to [Bichinger Software & Consulting](https://www.bichinger.de) for contributing OEM License upgrade to [Redactor 3](http://imperavi.com/redactor) + +![Bichinger Software & Consulting](doc/bichinger_logo.png) + --- Copyright 2010-2018 Oleg Khabarov. Released under the [MIT license](LICENSE) diff --git a/doc/bichinger_logo.png b/doc/bichinger_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ebc79eb32a8695354e2abec38cd9f6a04752b52 GIT binary patch literal 2702 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 X_ViL7mm&7wA4^>=L(Q_gn8<$sQQ Date: Thu, 4 Jul 2019 21:45:04 +0200 Subject: [PATCH 02/14] added redactor 3.1.8 javascript and css files --- .../javascripts/comfy/vendor/redactor.js | 27679 ++++++++++------ .../comfy/vendor/redactor/alignment.js | 55 + .../comfy/vendor/redactor/beyondgrammar.js | 66 + .../comfy/vendor/redactor/clips.js | 109 + .../comfy/vendor/redactor/counter.js | 77 + .../comfy/vendor/redactor/definedlinks.js | 135 +- .../comfy/vendor/redactor/filemanager.js | 152 +- .../comfy/vendor/redactor/fontcolor.js | 184 + .../comfy/vendor/redactor/fontfamily.js | 59 + .../comfy/vendor/redactor/fontsize.js | 58 + .../comfy/vendor/redactor/fullscreen.js | 156 + .../comfy/vendor/redactor/handle.js | 199 + .../comfy/vendor/redactor/i18n/ar.js | 140 +- .../comfy/vendor/redactor/i18n/az.js | 75 - .../comfy/vendor/redactor/i18n/ba.js | 79 - .../comfy/vendor/redactor/i18n/bg.js | 77 - .../comfy/vendor/redactor/i18n/by.js | 77 - .../comfy/vendor/redactor/i18n/ca.js | 75 - .../comfy/vendor/redactor/i18n/cs.js | 151 +- .../comfy/vendor/redactor/i18n/da.js | 139 +- .../comfy/vendor/redactor/i18n/de.js | 144 +- .../comfy/vendor/redactor/i18n/el.js | 76 - .../comfy/vendor/redactor/i18n/en.js | 139 +- .../comfy/vendor/redactor/i18n/eo.js | 76 - .../comfy/vendor/redactor/i18n/es.js | 139 +- .../comfy/vendor/redactor/i18n/es_AR.js | 76 - .../comfy/vendor/redactor/i18n/fa.js | 140 +- .../comfy/vendor/redactor/i18n/fi.js | 139 +- .../comfy/vendor/redactor/i18n/fr.js | 139 +- .../comfy/vendor/redactor/i18n/ge.js | 75 - .../comfy/vendor/redactor/i18n/gr.js | 75 - .../comfy/vendor/redactor/i18n/he.js | 139 +- .../comfy/vendor/redactor/i18n/hr.js | 76 - .../comfy/vendor/redactor/i18n/hu.js | 141 +- .../comfy/vendor/redactor/i18n/id.js | 76 - .../comfy/vendor/redactor/i18n/it.js | 142 +- .../comfy/vendor/redactor/i18n/ja.js | 140 +- .../comfy/vendor/redactor/i18n/kn.js | 75 - .../comfy/vendor/redactor/i18n/ko.js | 140 +- .../comfy/vendor/redactor/i18n/lt.js | 75 - .../comfy/vendor/redactor/i18n/lv.js | 75 - .../comfy/vendor/redactor/i18n/mk.js | 75 - .../comfy/vendor/redactor/i18n/nb.js | 75 - .../comfy/vendor/redactor/i18n/nl.js | 144 +- .../comfy/vendor/redactor/i18n/no.js | 64 + .../comfy/vendor/redactor/i18n/pl.js | 140 +- .../comfy/vendor/redactor/i18n/pt-BR.js | 75 - .../comfy/vendor/redactor/i18n/pt.js | 75 - .../comfy/vendor/redactor/i18n/pt_br.js | 64 + .../comfy/vendor/redactor/i18n/ro.js | 76 - .../comfy/vendor/redactor/i18n/ru.js | 140 +- .../comfy/vendor/redactor/i18n/sk.js | 140 +- .../comfy/vendor/redactor/i18n/sl.js | 143 +- .../comfy/vendor/redactor/i18n/sq.js | 79 - .../comfy/vendor/redactor/i18n/sr-CIR.js | 79 - .../comfy/vendor/redactor/i18n/sr-LAT.js | 79 - .../comfy/vendor/redactor/i18n/sv.js | 166 +- .../comfy/vendor/redactor/i18n/th.js | 74 - .../comfy/vendor/redactor/i18n/tr.js | 139 +- .../comfy/vendor/redactor/i18n/ua.js | 77 - .../comfy/vendor/redactor/i18n/uk.js | 77 - .../comfy/vendor/redactor/i18n/vi.js | 74 - .../comfy/vendor/redactor/i18n/zh-CN.js | 75 - .../comfy/vendor/redactor/i18n/zh-TW.js | 75 - .../comfy/vendor/redactor/i18n/zh_cn.js | 64 + .../comfy/vendor/redactor/i18n/zh_tw.js | 64 + .../comfy/vendor/redactor/imagemanager.js | 137 +- .../comfy/vendor/redactor/inlinestyle.js | 62 + .../comfy/vendor/redactor/limiter.js | 87 + .../comfy/vendor/redactor/properties.js | 190 + .../comfy/vendor/redactor/specialchars.js | 78 + .../comfy/vendor/redactor/table.js | 786 +- .../comfy/vendor/redactor/textdirection.js | 35 + .../comfy/vendor/redactor/textexpander.js | 64 + .../comfy/vendor/redactor/variable.js | 222 + .../comfy/vendor/redactor/video.js | 198 +- .../comfy/vendor/redactor/widget.js | 153 + .../comfy/admin/cms/application.sass | 5 + .../stylesheets/comfy/vendor/redactor.css | 2157 +- .../comfy/vendor/redactor/clips.css | 20 + .../comfy/vendor/redactor/filemanager.css | 38 + .../comfy/vendor/redactor/handle.css | 27 + .../comfy/vendor/redactor/inlinestyle.css | 34 + .../comfy/vendor/redactor/variable.css | 23 + 84 files changed, 23915 insertions(+), 14773 deletions(-) mode change 100755 => 100644 app/assets/javascripts/comfy/vendor/redactor.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/alignment.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/beyondgrammar.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/clips.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/counter.js mode change 100755 => 100644 app/assets/javascripts/comfy/vendor/redactor/filemanager.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/fontcolor.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/fontfamily.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/fontsize.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/fullscreen.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/handle.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/az.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/ba.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/bg.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/by.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/ca.js mode change 100644 => 100755 app/assets/javascripts/comfy/vendor/redactor/i18n/cs.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/el.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/eo.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/es_AR.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/ge.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/gr.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/hr.js mode change 100644 => 100755 app/assets/javascripts/comfy/vendor/redactor/i18n/hu.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/id.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/kn.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/lt.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/lv.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/mk.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/nb.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/no.js mode change 100644 => 100755 app/assets/javascripts/comfy/vendor/redactor/i18n/pl.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/pt-BR.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/pt.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/pt_br.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/ro.js mode change 100644 => 100755 app/assets/javascripts/comfy/vendor/redactor/i18n/sk.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/sq.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/sr-CIR.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/sr-LAT.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/th.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/ua.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/uk.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/vi.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/zh-CN.js delete mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/zh-TW.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/zh_cn.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/i18n/zh_tw.js mode change 100755 => 100644 app/assets/javascripts/comfy/vendor/redactor/imagemanager.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/inlinestyle.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/limiter.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/properties.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/specialchars.js mode change 100755 => 100644 app/assets/javascripts/comfy/vendor/redactor/table.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/textdirection.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/textexpander.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/variable.js mode change 100755 => 100644 app/assets/javascripts/comfy/vendor/redactor/video.js create mode 100644 app/assets/javascripts/comfy/vendor/redactor/widget.js mode change 100755 => 100644 app/assets/stylesheets/comfy/vendor/redactor.css create mode 100644 app/assets/stylesheets/comfy/vendor/redactor/clips.css create mode 100644 app/assets/stylesheets/comfy/vendor/redactor/filemanager.css create mode 100644 app/assets/stylesheets/comfy/vendor/redactor/handle.css create mode 100644 app/assets/stylesheets/comfy/vendor/redactor/inlinestyle.css create mode 100644 app/assets/stylesheets/comfy/vendor/redactor/variable.css diff --git a/app/assets/javascripts/comfy/vendor/redactor.js b/app/assets/javascripts/comfy/vendor/redactor.js old mode 100755 new mode 100644 index 955dc1a5e..4cf57078e --- a/app/assets/javascripts/comfy/vendor/redactor.js +++ b/app/assets/javascripts/comfy/vendor/redactor.js @@ -1,9561 +1,18176 @@ /* - Redactor 10.2.5 - Updated: October 1, 2015 + Redactor + Version 3.1.8 + Updated: April 3, 2019 - http://imperavi.com/redactor/ + http://imperavi.com/redactor/ - Copyright (c) 2009-2015, Imperavi LLC. - License: http://imperavi.com/redactor/license/ - - Usage: $('#content').redactor(); - - NOTE: This is the most recent editor we have licence for. There are inline - fixes and tweaks annotated with "COMFY FIX" + Copyright (c) 2009-2019, Imperavi Ltd. + License: http://imperavi.com/redactor/license/ */ +(function() { +var Ajax = {}; -(function($) -{ +Ajax.settings = {}; +Ajax.post = function(options) { return new AjaxRequest('post', options); }; +Ajax.get = function(options) { return new AjaxRequest('get', options); }; - 'use strict'; +var AjaxRequest = function(method, options) +{ + var defaults = { + method: method, + url: '', + before: function() {}, + success: function() {}, + error: function() {}, + data: false, + async: true, + headers: {} + }; + + this.p = this.extend(defaults, options); + this.p = this.extend(this.p, Ajax.settings); + this.p.method = this.p.method.toUpperCase(); + + this.prepareData(); + + this.xhr = new XMLHttpRequest(); + this.xhr.open(this.p.method, this.p.url, this.p.async); + + this.setHeaders(); + + var before = (typeof this.p.before === 'function') ? this.p.before(this.xhr) : true; + if (before !== false) + { + this.send(); + } +}; + +AjaxRequest.prototype = { + extend: function(obj1, obj2) + { + if (obj2) for (var name in obj2) { obj1[name] = obj2[name]; } + return obj1; + }, + prepareData: function() + { + if (this.p.method === 'POST' && !this.isFormData()) this.p.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + if (typeof this.p.data === 'object' && !this.isFormData()) this.p.data = this.toParams(this.p.data); + if (this.p.method === 'GET') this.p.url = (this.p.data) ? this.p.url + '?' + this.p.data : this.p.url; + }, + setHeaders: function() + { + this.xhr.setRequestHeader('X-Requested-With', this.p.headers['X-Requested-With'] || 'XMLHttpRequest'); + for (var name in this.p.headers) + { + this.xhr.setRequestHeader(name, this.p.headers[name]); + } + }, + isFormData: function() + { + return (typeof window.FormData !== 'undefined' && this.p.data instanceof window.FormData); + }, + isComplete: function() + { + return !(this.xhr.status < 200 || this.xhr.status >= 300 && this.xhr.status !== 304); + }, + send: function() + { + if (this.p.async) + { + this.xhr.onload = this.loaded.bind(this); + this.xhr.send(this.p.data); + } + else + { + this.xhr.send(this.p.data); + this.loaded.call(this); + } + }, + loaded: function() + { + if (this.isComplete()) + { + var response = this.xhr.response; + var json = this.parseJson(response); + response = (json) ? json : response; + + if (typeof this.p.success === 'function') this.p.success(response, this.xhr); + } + else + { + if (typeof this.p.error === 'function') this.p.error(this.xhr.statusText); + } + }, + parseJson: function(str) + { + try { + var o = JSON.parse(str); + if (o && typeof o === 'object') + { + return o; + } + + } catch (e) {} + + return false; + }, + toParams: function (obj) + { + return Object.keys(obj).map( + function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(obj[k]); } + ).join('&'); + } +}; +var DomCache = [0]; +var DomExpando = 'data' + new Date().getTime(); +var DomHClass = 'is-hidden'; +var DomHMClass = 'is-hidden-mobile'; + +var Dom = function(selector, context) +{ + return this.parse(selector, context); +}; - if (!Function.prototype.bind) - { - Function.prototype.bind = function(scope) - { - var fn = this; - return function() - { - return fn.apply(scope); - }; +Dom.ready = function(fn) +{ + if (document.readyState != 'loading') fn(); + else document.addEventListener('DOMContentLoaded', fn); +}; + +Dom.prototype = { + get dom() + { + return true; + }, + get length() + { + return this.nodes.length; + }, + parse: function(selector, context) + { + var nodes; + var reHtmlTest = /^\s*<(\w+|!)[^>]*>/; + + if (!selector) + { + nodes = []; + } + else if (selector.dom) + { + this.nodes = selector.nodes; + return selector; + } + else if (typeof selector !== 'string') + { + if (selector.nodeType && selector.nodeType === 11) + { + nodes = selector.childNodes; + } + else + { + nodes = (selector.nodeType || selector === window) ? [selector] : selector; + } + } + else if (reHtmlTest.test(selector)) + { + nodes = this.create(selector); + } + else + { + nodes = this._query(selector, context); + } + + this.nodes = this._slice(nodes); + }, + create: function(html) + { + if (/^<(\w+)\s*\/?>(?:<\/\1>|)$/.test(html)) + { + return [document.createElement(RegExp.$1)]; + } + + var elements = []; + var container = document.createElement('div'); + var children = container.childNodes; + + container.innerHTML = html; + + for (var i = 0, l = children.length; i < l; i++) + { + elements.push(children[i]); + } + + return elements; + }, + + // add + add: function(nodes) + { + this.nodes = this.nodes.concat(this._toArray(nodes)); + }, + + // get + get: function(index) + { + return this.nodes[(index || 0)] || false; + }, + getAll: function() + { + return this.nodes; + }, + eq: function(index) + { + return new Dom(this.nodes[index]); + }, + first: function() + { + return new Dom(this.nodes[0]); + }, + last: function() + { + return new Dom(this.nodes[this.nodes.length - 1]); + }, + contents: function() + { + return this.get().childNodes; + }, + + // loop + each: function(callback) + { + var len = this.nodes.length; + for (var i = 0; i < len; i++) + { + callback.call(this, (this.nodes[i].dom) ? this.nodes[i].get() : this.nodes[i], i); + } + + return this; + }, + + // traversing + is: function(selector) + { + return (this.filter(selector).length > 0); + }, + filter: function (selector) + { + var callback; + if (selector === undefined) + { + return this; + } + else if (typeof selector === 'function') + { + callback = selector; + } + else + { + callback = function(node) + { + if (selector instanceof Node) + { + return (selector === node); + } + else if (selector && selector.dom) + { + return ((selector.nodes).indexOf(node) !== -1); + } + else + { + node.matches = node.matches || node.msMatchesSelector || node.webkitMatchesSelector; + return (node.nodeType === 1) ? node.matches(selector || '*') : false; + } + }; + } + + return new Dom(this.nodes.filter(callback)); + }, + not: function(filter) + { + return this.filter(function(node) + { + return !new Dom(node).is(filter || true); + }); + }, + find: function(selector) + { + var nodes = []; + this.each(function(node) + { + var ns = this._query(selector || '*', node); + for (var i = 0; i < ns.length; i++) + { + nodes.push(ns[i]); + } + }); + + return new Dom(nodes); + }, + children: function(selector) + { + var nodes = []; + this.each(function(node) + { + if (node.children) + { + var ns = node.children; + for (var i = 0; i < ns.length; i++) + { + nodes.push(ns[i]); + } + } + }); + + return new Dom(nodes).filter(selector); + }, + parent: function(selector) + { + var nodes = []; + this.each(function(node) + { + if (node.parentNode) nodes.push(node.parentNode); + }); + + return new Dom(nodes).filter(selector); + }, + parents: function(selector, context) + { + context = this._getContext(context); + + var nodes = []; + this.each(function(node) + { + var parent = node.parentNode; + while (parent && parent !== context) + { + if (selector) + { + if (new Dom(parent).is(selector)) { nodes.push(parent); } + } + else + { + nodes.push(parent); + } + + parent = parent.parentNode; + } + }); + + return new Dom(nodes); + }, + closest: function(selector, context) + { + context = this._getContext(context); + selector = (selector.dom) ? selector.get() : selector; + + var nodes = []; + var isNode = (selector && selector.nodeType); + this.each(function(node) + { + do { + if ((isNode && node === selector) || new Dom(node).is(selector)) return nodes.push(node); + } while ((node = node.parentNode) && node !== context); + }); + + return new Dom(nodes); + }, + next: function(selector) + { + return this._getSibling(selector, 'nextSibling'); + }, + nextElement: function(selector) + { + return this._getSibling(selector, 'nextElementSibling'); + }, + prev: function(selector) + { + return this._getSibling(selector, 'previousSibling'); + }, + prevElement: function(selector) + { + return this._getSibling(selector, 'previousElementSibling'); + }, + + // css + css: function(name, value) + { + if (value === undefined && (typeof name !== 'object')) + { + var node = this.get(); + if (name === 'width' || name === 'height') + { + return (node.style) ? this._getHeightOrWidth(name, node, false) + 'px' : undefined; + } + else + { + return (node.style) ? getComputedStyle(node, null)[name] : undefined; + } + } + + // set + return this.each(function(node) + { + var obj = {}; + if (typeof name === 'object') obj = name; + else obj[name] = value; + + for (var key in obj) + { + if (node.style) node.style[key] = obj[key]; + } + }); + }, + + // attr + attr: function(name, value, data) + { + data = (data) ? 'data-' : ''; + + if (value === undefined && (typeof name !== 'object')) + { + var node = this.get(); + if (node && node.nodeType !== 3) + { + return (name === 'checked') ? node.checked : this._getBooleanFromStr(node.getAttribute(data + name)); + } + else return; + } + + // set + return this.each(function(node) + { + var obj = {}; + if (typeof name === 'object') obj = name; + else obj[name] = value; + + for (var key in obj) + { + if (node.nodeType !== 3) + { + if (key === 'checked') node.checked = obj[key]; + else node.setAttribute(data + key, obj[key]); + } + } + }); + }, + data: function(name, value) + { + if (name === undefined) + { + var reDataAttr = /^data\-(.+)$/; + var attrs = this.get().attributes; + + var data = {}; + var replacer = function (g) { return g[1].toUpperCase(); }; + + for (var key in attrs) + { + if (attrs[key] && reDataAttr.test(attrs[key].nodeName)) + { + var dataName = attrs[key].nodeName.match(reDataAttr)[1]; + var val = attrs[key].value; + dataName = dataName.replace(/-([a-z])/g, replacer); + + if (this._isObjectString(val)) val = this._toObject(val); + else val = (this._isNumber(val)) ? parseFloat(val) : this._getBooleanFromStr(val); + + data[dataName] = val; + } + } + + return data; + } + + return this.attr(name, value, true); + }, + val: function(value) + { + if (value === undefined) + { + var el = this.get(); + if (el.type && el.type === 'checkbox') return el.checked; + else return el.value; + } + + return this.each(function(node) + { + node.value = value; + }); + }, + removeAttr: function(value) + { + return this.each(function(node) + { + var rmAttr = function(name) { if (node.nodeType !== 3) node.removeAttribute(name); }; + value.split(' ').forEach(rmAttr); + }); + }, + removeData: function(value) + { + return this.each(function(node) + { + var rmData = function(name) { if (node.nodeType !== 3) node.removeAttribute('data-' + name); }; + value.split(' ').forEach(rmData); + }); + }, + + // dataset/dataget + dataset: function(key, value) + { + return this.each(function(node) + { + DomCache[this.dataindex(node)][key] = value; + }); + }, + dataget: function(key) + { + return DomCache[this.dataindex(this.get())][key]; + }, + dataindex: function(el) + { + var cacheIndex = el[DomExpando]; + var nextCacheIndex = DomCache.length; + + if (!cacheIndex) + { + cacheIndex = el[DomExpando] = nextCacheIndex; + DomCache[cacheIndex] = {}; + } + + return cacheIndex; + }, + + + // class + addClass: function(value) + { + return this._eachClass(value, 'add'); + }, + removeClass: function(value) + { + return this._eachClass(value, 'remove'); + }, + toggleClass: function(value) + { + return this._eachClass(value, 'toggle'); + }, + hasClass: function(value) + { + return this.nodes.some(function(node) + { + return (node.classList) ? node.classList.contains(value) : false; + }); + }, + + // html & text + empty: function() + { + return this.each(function(node) + { + node.innerHTML = ''; + }); + }, + html: function(html) + { + return (html === undefined) ? (this.get().innerHTML || '') : this.empty().append(html); + }, + text: function(text) + { + return (text === undefined) ? (this.get().textContent || '') : this.each(function(node) { node.textContent = text; }); + }, + + // manipulation + after: function(html) + { + return this._inject(html, function(frag, node) + { + if (typeof frag === 'string') + { + node.insertAdjacentHTML('afterend', frag); + } + else + { + var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse(); + for (var i = 0; i < elms.length; i++) + { + node.parentNode.insertBefore(elms[i], node.nextSibling); + } + } + + return node; + + }); + }, + before: function(html) + { + return this._inject(html, function(frag, node) + { + if (typeof frag === 'string') + { + node.insertAdjacentHTML('beforebegin', frag); + } + else + { + var elms = (frag instanceof Node) ? [frag] : this._toArray(frag); + for (var i = 0; i < elms.length; i++) + { + node.parentNode.insertBefore(elms[i], node); + } + } + + return node; + }); + }, + append: function(html) + { + return this._inject(html, function(frag, node) + { + if (typeof frag === 'string' || typeof frag === 'number') + { + node.insertAdjacentHTML('beforeend', frag); + } + else + { + var elms = (frag instanceof Node) ? [frag] : this._toArray(frag); + for (var i = 0; i < elms.length; i++) + { + node.appendChild(elms[i]); + } + } + + return node; + }); + }, + prepend: function(html) + { + return this._inject(html, function(frag, node) + { + if (typeof frag === 'string' || typeof frag === 'number') + { + node.insertAdjacentHTML('afterbegin', frag); + } + else + { + var elms = (frag instanceof Node) ? [frag] : this._toArray(frag).reverse(); + for (var i = 0; i < elms.length; i++) + { + node.insertBefore(elms[i], node.firstChild); + } + } + + return node; + }); + }, + wrap: function(html) + { + return this._inject(html, function(frag, node) + { + var wrapper = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag)[0] : (frag instanceof Node) ? frag : this._toArray(frag)[0]; + + if (node.parentNode) + { + node.parentNode.insertBefore(wrapper, node); + } + + wrapper.appendChild(node); + + return new Dom(wrapper); + + }); + }, + unwrap: function() + { + return this.each(function(node) + { + var $node = new Dom(node); + + return $node.replaceWith($node.contents()); + }); + }, + replaceWith: function(html) + { + return this._inject(html, function(frag, node) + { + var docFrag = document.createDocumentFragment(); + var elms = (typeof frag === 'string' || typeof frag === 'number') ? this.create(frag) : (frag instanceof Node) ? [frag] : this._toArray(frag); + + for (var i = 0; i < elms.length; i++) + { + docFrag.appendChild(elms[i]); + } + + var result = docFrag.childNodes[0]; + node.parentNode.replaceChild(docFrag, node); + + return result; + + }); + }, + remove: function() + { + return this.each(function(node) + { + if (node.parentNode) node.parentNode.removeChild(node); + }); + }, + clone: function(events) + { + var nodes = []; + this.each(function(node) + { + var copy = this._clone(node); + if (events) copy = this._cloneEvents(node, copy); + nodes.push(copy); + }); + + return new Dom(nodes); + }, + + // show/hide + show: function() + { + return this.each(function(node) + { + if (!node.style || !this._hasDisplayNone(node)) return; + + var target = node.getAttribute('domTargetShow'); + var isHidden = (node.classList) ? node.classList.contains(DomHClass) : false; + var isHiddenMobile = (node.classList) ? node.classList.contains(DomHMClass) : false; + var type; + + if (isHidden) + { + type = DomHClass; + node.classList.remove(DomHClass); + } + else if (isHiddenMobile) + { + type = DomHMClass; + node.classList.remove(DomHMClass); + } + else + { + node.style.display = (target) ? target : 'block'; + } + + if (type) node.setAttribute('domTargetHide', type); + node.removeAttribute('domTargetShow'); + + }.bind(this)); + }, + hide: function() + { + return this.each(function(node) + { + if (!node.style || this._hasDisplayNone(node)) return; + + var display = node.style.display; + var target = node.getAttribute('domTargetHide'); + + if (target === DomHClass) + { + node.classList.add(DomHClass); + } + else if (target === DomHMClass) + { + node.classList.add(DomHMClass); + } + else + { + if (display !== 'block') node.setAttribute('domTargetShow', display); + node.style.display = 'none'; + } + + node.removeAttribute('domTargetHide'); + + }); + }, + + // dimensions + scrollTop: function(value) + { + var node = this.get(); + var isWindow = (node === window); + var isDocument = (node.nodeType === 9); + var el = (isDocument) ? (document.scrollingElement || document.body.parentNode || document.body || document.documentElement) : node; + + if (value !== undefined) + { + if (isWindow) window.scrollTo(0, value); + else el.scrollTop = value; + return; + } + + if (isDocument) + { + return (typeof window.pageYOffset != 'undefined') ? window.pageYOffset : ((document.documentElement.scrollTop) ? document.documentElement.scrollTop : ((document.body.scrollTop) ? document.body.scrollTop : 0)); + } + else + { + return (isWindow) ? window.pageYOffset : el.scrollTop; + } + }, + offset: function() + { + return this._getDim('Offset'); + }, + position: function() + { + return this._getDim('Position'); + }, + width: function(value, adjust) + { + return this._getSize('width', 'Width', value, adjust); + }, + height: function(value, adjust) + { + return this._getSize('height', 'Height', value, adjust); + }, + outerWidth: function() + { + return this._getInnerOrOuter('width', 'outer'); + }, + outerHeight: function() + { + return this._getInnerOrOuter('height', 'outer'); + }, + innerWidth: function() + { + return this._getInnerOrOuter('width', 'inner'); + }, + innerHeight: function() + { + return this._getInnerOrOuter('height', 'inner'); + }, + + // events + click: function() + { + return this._triggerEvent('click'); + }, + focus: function() + { + return this._triggerEvent('focus'); + }, + trigger: function(names) + { + return this.each(function(node) + { + var events = names.split(' '); + for (var i = 0; i < events.length; i++) + { + var ev; + var opts = { bubbles: true, cancelable: true }; + + try { + ev = new window.CustomEvent(events[i], opts); + } catch(e) { + ev = document.createEvent('CustomEvent'); + ev.initCustomEvent(events[i], true, true); + } + + node.dispatchEvent(ev); + } + }); + }, + on: function(names, handler, one) + { + return this.each(function(node) + { + var events = names.split(' '); + for (var i = 0; i < events.length; i++) + { + var event = this._getEventName(events[i]); + var namespace = this._getEventNamespace(events[i]); + + handler = (one) ? this._getOneHandler(handler, names) : handler; + node.addEventListener(event, handler); + + node._e = node._e || {}; + node._e[namespace] = node._e[namespace] || {}; + node._e[namespace][event] = node._e[namespace][event] || []; + node._e[namespace][event].push(handler); + } + + }); + }, + one: function(events, handler) + { + return this.on(events, handler, true); + }, + off: function(names, handler) + { + var testEvent = function(name, key, event) { return (name === event); }; + var testNamespace = function(name, key, event, namespace) { return (key === namespace); }; + var testEventNamespace = function(name, key, event, namespace) { return (name === event && key === namespace); }; + var testPositive = function() { return true; }; + + if (names === undefined) + { + // ALL + return this.each(function(node) + { + this._offEvent(node, false, false, handler, testPositive); + }); + } + + return this.each(function(node) + { + var events = names.split(' '); + + for (var i = 0; i < events.length; i++) + { + var event = this._getEventName(events[i]); + var namespace = this._getEventNamespace(events[i]); + + // 1) event without namespace + if (namespace === '_events') this._offEvent(node, event, namespace, handler, testEvent); + // 2) only namespace + else if (!event && namespace !== '_events') this._offEvent(node, event, namespace, handler, testNamespace); + // 3) event + namespace + else this._offEvent(node, event, namespace, handler, testEventNamespace); + } + }); + }, + + // form + serialize: function(asObject) + { + var obj = {}; + var elms = this.get().elements; + for (var i = 0; i < elms.length; i++) + { + var el = elms[i]; + if (/(checkbox|radio)/.test(el.type) && !el.checked) continue; + if (!el.name || el.disabled || el.type === 'file') continue; + + if (el.type === 'select-multiple') + { + for (var z = 0; z < el.options.length; z++) + { + var opt = el.options[z]; + if (opt.selected) obj[el.name] = opt.value; + } + } + + obj[el.name] = (this._isNumber(el.value)) ? parseFloat(el.value) : this._getBooleanFromStr(el.value); + } + + return (asObject) ? obj : this._toParams(obj); + }, + ajax: function(success, error) + { + if (typeof AjaxRequest !== 'undefined') + { + var method = this.attr('method') || 'post'; + var options = { + url: this.attr('action'), + data: this.serialize(), + success: success, + error: error + }; + + return new AjaxRequest(method, options); + } + }, + + // private + _queryContext: function(selector, context) + { + context = this._getContext(context); + + return (context.nodeType !== 3 && typeof context.querySelectorAll === 'function') ? context.querySelectorAll(selector) : []; + }, + _query: function(selector, context) + { + if (context) + { + return this._queryContext(selector, context); + } + else if (/^[.#]?[\w-]*$/.test(selector)) + { + if (selector[0] === '#') + { + var element = document.getElementById(selector.slice(1)); + return element ? [element] : []; + } + + if (selector[0] === '.') + { + return document.getElementsByClassName(selector.slice(1)); + } + + return document.getElementsByTagName(selector); + } + + return document.querySelectorAll(selector); + }, + _getContext: function(context) + { + context = (typeof context === 'string') ? document.querySelector(context) : context; + + return (context && context.dom) ? context.get() : (context || document); + }, + _inject: function(html, fn) + { + var len = this.nodes.length; + var nodes = []; + while (len--) + { + var res = (typeof html === 'function') ? html.call(this, this.nodes[len]) : html; + var el = (len === 0) ? res : this._clone(res); + var node = fn.call(this, el, this.nodes[len]); + + if (node) + { + if (node.dom) nodes.push(node.get()); + else nodes.push(node); + } + } + + return new Dom(nodes); + }, + _cloneEvents: function(node, copy) + { + var events = node._e; + if (events) + { + copy._e = events; + for (var name in events._events) + { + for (var i = 0; i < events._events[name].length; i++) + { + copy.addEventListener(name, events._events[name][i]); + } + } + } + + return copy; + }, + _clone: function(node) + { + if (typeof node === 'undefined') return; + if (typeof node === 'string') return node; + else if (node instanceof Node || node.nodeType) return node.cloneNode(true); + else if ('length' in node) + { + return [].map.call(this._toArray(node), function(el) { return el.cloneNode(true); }); + } + }, + _slice: function(obj) + { + return (!obj || obj.length === 0) ? [] : (obj.length) ? [].slice.call(obj.nodes || obj) : [obj]; + }, + _eachClass: function(value, type) + { + return this.each(function(node) + { + if (value) + { + var setClass = function(name) { if (node.classList) node.classList[type](name); }; + value.split(' ').forEach(setClass); + } + }); + }, + _triggerEvent: function(name) + { + var node = this.get(); + if (node && node.nodeType !== 3) node[name](); + return this; + }, + _getOneHandler: function(handler, events) + { + var self = this; + return function() + { + handler.apply(this, arguments); + self.off(events); + }; + }, + _getEventNamespace: function(event) + { + var arr = event.split('.'); + var namespace = (arr[1]) ? arr[1] : '_events'; + return (arr[2]) ? namespace + arr[2] : namespace; + }, + _getEventName: function(event) + { + return event.split('.')[0]; + }, + _offEvent: function(node, event, namespace, handler, condition) + { + for (var key in node._e) + { + for (var name in node._e[key]) + { + if (condition(name, key, event, namespace)) + { + var handlers = node._e[key][name]; + for (var i = 0; i < handlers.length; i++) + { + if (typeof handler !== 'undefined' && handlers[i].toString() !== handler.toString()) + { + continue; + } + + node.removeEventListener(name, handlers[i]); + node._e[key][name].splice(i, 1); + + if (node._e[key][name].length === 0) delete node._e[key][name]; + if (Object.keys(node._e[key]).length === 0) delete node._e[key]; + } + } + } + } + }, + _getInnerOrOuter: function(method, type) + { + return this[method](undefined, type); + }, + _getDocSize: function(node, type) + { + var body = node.body, html = node.documentElement; + return Math.max(body['scroll' + type], body['offset' + type], html['client' + type], html['scroll' + type], html['offset' + type]); + }, + _getSize: function(type, captype, value, adjust) + { + if (value === undefined) + { + var el = this.get(); + if (el.nodeType === 3) value = 0; + else if (el.nodeType === 9) value = this._getDocSize(el, captype); + else if (el === window) value = window['inner' + captype]; + else value = this._getHeightOrWidth(type, el, adjust || 'normal'); + + return Math.round(value); + } + + return this.each(function(node) + { + value = parseFloat(value); + value = value + this._adjustResultHeightOrWidth(type, node, adjust || 'normal'); + + new Dom(node).css(type, value + 'px'); + + }.bind(this)); + }, + _getHeightOrWidth: function(type, el, adjust) + { + if (!el) return 0; + + var name = type.charAt(0).toUpperCase() + type.slice(1); + var result = 0; + var style = getComputedStyle(el, null); + var $el = new Dom(el); + var $targets = $el.parents().filter(function(node) + { + return (node.nodeType === 1 && getComputedStyle(node, null).display === 'none') ? node : false; + }); + + if (style.display === 'none') $targets.add(el); + if ($targets.length !== 0) + { + var fixStyle = 'visibility: hidden !important; display: block !important;'; + var tmp = []; + + $targets.each(function(node) + { + var $node = new Dom(node); + var thisStyle = $node.attr('style'); + if (thisStyle !== null) tmp.push(thisStyle); + $node.attr('style', (thisStyle !== null) ? thisStyle + ';' + fixStyle : fixStyle); + }); + + result = $el.get()['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust); + + $targets.each(function(node, i) + { + var $node = new Dom(node); + if (tmp[i] === undefined) $node.removeAttr('style'); + else $node.attr('style', tmp[i]); + }); + } + else + { + result = el['offset' + name] - this._adjustResultHeightOrWidth(type, el, adjust); + } + + return result; + }, + _adjustResultHeightOrWidth: function(type, el, adjust) + { + if (!el || adjust === false) return 0; + + var fix = 0; + var style = getComputedStyle(el, null); + var isBorderBox = (style.boxSizing === "border-box"); + + if (type === 'height') + { + if (adjust === 'inner' || (adjust === 'normal' && isBorderBox)) + { + fix += (parseFloat(style.borderTopWidth) || 0) + (parseFloat(style.borderBottomWidth) || 0); + } + + if (adjust === 'outer') fix -= (parseFloat(style.marginTop) || 0) + (parseFloat(style.marginBottom) || 0); + } + else + { + if (adjust === 'inner' || (adjust === 'normal' && isBorderBox)) + { + fix += (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.borderRightWidth) || 0); + } + + if (adjust === 'outer') fix -= (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0); + } + + return fix; + }, + _getDim: function(type) + { + var node = this.get(); + return (node.nodeType === 3) ? { top: 0, left: 0 } : this['_get' + type](node); + }, + _getPosition: function(node) + { + return { top: node.offsetTop, left: node.offsetLeft }; + }, + _getOffset: function(node) + { + var rect = node.getBoundingClientRect(); + var doc = node.ownerDocument; + var docElem = doc.documentElement; + var win = doc.defaultView; + + return { + top: rect.top + win.pageYOffset - docElem.clientTop, + left: rect.left + win.pageXOffset - docElem.clientLeft }; - } - - var uuid = 0; - - // Plugin - $.fn.redactor = function(options) - { - var val = []; - var args = Array.prototype.slice.call(arguments, 1); - - if (typeof options === 'string') - { - this.each(function() - { - var instance = $.data(this, 'redactor'); - var func; - - if (options.search(/\./) != '-1') - { - func = options.split('.'); - if (typeof instance[func[0]] != 'undefined') - { - func = instance[func[0]][func[1]]; - } - } - else - { - func = instance[options]; - } - - if (typeof instance !== 'undefined' && $.isFunction(func)) - { - var methodVal = func.apply(instance, args); - if (methodVal !== undefined && methodVal !== instance) - { - val.push(methodVal); - } - } - else - { - $.error('No such method "' + options + '" for Redactor'); - } - }); - } - else - { - this.each(function() - { - $.data(this, 'redactor', {}); - $.data(this, 'redactor', Redactor(this, options)); - }); - } - - if (val.length === 0) return this; - else if (val.length === 1) return val[0]; - else return val; - - }; - - // Initialization - function Redactor(el, options) - { - return new Redactor.prototype.init(el, options); - } - - // Functionality - $.Redactor = Redactor; - $.Redactor.VERSION = '10.2.5'; - $.Redactor.modules = ['alignment', 'autosave', 'block', 'buffer', 'build', 'button', - 'caret', 'clean', 'code', 'core', 'dropdown', 'file', 'focus', - 'image', 'indent', 'inline', 'insert', 'keydown', 'keyup', - 'lang', 'line', 'link', 'linkify', 'list', 'modal', 'observe', 'paragraphize', - 'paste', 'placeholder', 'progress', 'selection', 'shortcuts', - 'tabifier', 'tidy', 'toolbar', 'upload', 'utils']; - - $.Redactor.opts = { - - // settings - lang: 'en', - direction: 'ltr', // ltr or rtl - - plugins: false, // array - - focus: false, - focusEnd: false, - - placeholder: false, - - visual: true, - tabindex: false, + }, + _getSibling: function(selector, method) + { + selector = (selector && selector.dom) ? selector.get() : selector; + + var isNode = (selector && selector.nodeType); + var sibling; + + this.each(function(node) + { + while (node = node[method]) + { + if ((isNode && node === selector) || new Dom(node).is(selector)) + { + sibling = node; + return; + } + } + }); + + return new Dom(sibling); + }, + _toArray: function(obj) + { + if (obj instanceof NodeList) + { + var arr = []; + for (var i = 0; i < obj.length; i++) + { + arr[i] = obj[i]; + } + + return arr; + } + else if (obj === undefined) return []; + else + { + return (obj.dom) ? obj.nodes : obj; + } + }, + _toParams: function(obj) + { + var params = ''; + for (var key in obj) + { + params += '&' + this._encodeUri(key) + '=' + this._encodeUri(obj[key]); + } + + return params.replace(/^&/, ''); + }, + _toObject: function(str) + { + return (new Function("return " + str))(); + }, + _encodeUri: function(str) + { + return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+'); + }, + _isNumber: function(str) + { + return !isNaN(str) && !isNaN(parseFloat(str)); + }, + _isObjectString: function(str) + { + return (str.search(/^{/) !== -1); + }, + _getBooleanFromStr: function(str) + { + if (str === 'true') return true; + else if (str === 'false') return false; + + return str; + }, + _hasDisplayNone: function(el) + { + return (el.style.display === 'none') || ((el.currentStyle) ? el.currentStyle.display : getComputedStyle(el, null).display) === 'none'; + } +}; +// Unique ID +var uuid = 0; + +// Wrapper +var $R = function(selector, options) +{ + return RedactorApp(selector, options, [].slice.call(arguments, 2)); +}; + +// Globals +$R.app = []; +$R.version = '3.1.8'; +$R.options = {}; +$R.modules = {}; +$R.services = {}; +$R.classes = {}; +$R.plugins = {}; +$R.mixins = {}; +$R.modals = {}; +$R.lang = {}; +$R.dom = function(selector, context) { return new Dom(selector, context); }; +$R.ajax = Ajax; +$R.Dom = Dom; +$R.keycodes = { + BACKSPACE: 8, + DELETE: 46, + UP: 38, + DOWN: 40, + ENTER: 13, + SPACE: 32, + ESC: 27, + TAB: 9, + CTRL: 17, + META: 91, + SHIFT: 16, + ALT: 18, + RIGHT: 39, + LEFT: 37 +}; +$R.env = { + 'plugin': 'plugins', + 'module': 'modules', + 'service': 'services', + 'class': 'classes', + 'mixin': 'mixins' +}; + +// jQuery Wrapper +/*eslint-env jquery*/ +if (typeof jQuery !== 'undefined') +{ + (function($) { $.fn.redactor = function(options) { return RedactorApp(this.toArray(), options, [].slice.call(arguments, 1)); }; })(jQuery); +} - minHeight: false, - maxHeight: false, +// Class +var RedactorApp = function(selector, options, args) +{ + var namespace = 'redactor'; + var nodes = (Array.isArray(selector)) ? selector : (selector && selector.nodeType) ? [selector] : document.querySelectorAll(selector); + var isApi = (typeof options === 'string' || typeof options === 'function'); + var value = []; + var instance; + + for (var i = 0; i < nodes.length; i++) + { + var el = nodes[i]; + var $el = $R.dom(el); + + instance = $el.dataget(namespace); + if (!instance && !isApi) + { + // Initialization + instance = new App(el, options, uuid); + $el.dataset(namespace, instance); + $R.app[uuid] = instance; + uuid++; + } + + // API + if (instance && isApi) + { + var isDestroy = (options === 'destroy'); + options = (isDestroy) ? 'stop' : options; + + var methodValue; + if (typeof options === 'function') + { + methodValue = options.apply(instance, args); + } + else + { + args.unshift(options); + methodValue = instance.api.apply(instance, args); + } + if (methodValue !== undefined) value.push(methodValue); + + if (isDestroy) $el.dataset(namespace, false); + } + } + + return (value.length === 0 || value.length === 1) ? ((value.length === 0) ? instance : value[0]) : value; +}; + +// add +$R.add = function(type, name, obj) +{ + if (typeof $R.env[type] === 'undefined') return; + + // translations + if (obj.translations) + { + $R.lang = $R.extend(true, {}, $R.lang, obj.translations); + } + + // modals + if (obj.modals) + { + $R.modals = $R.extend(true, {}, $R.modals, obj.modals); + } + + // mixin + if (type === 'mixin') + { + $R[$R.env[type]][name] = obj; + } + else + { + // prototype + var F = function() {}; + F.prototype = obj; + + // mixins + if (obj.mixins) + { + for (var i = 0; i < obj.mixins.length; i++) + { + $R.inherit(F, $R.mixins[obj.mixins[i]]); + } + } + + $R[$R.env[type]][name] = F; + } +}; + +// add lang +$R.addLang = function(lang, obj) +{ + if (typeof $R.lang[lang] === 'undefined') + { + $R.lang[lang] = {}; + } - linebreaks: false, - replaceDivs: true, - paragraphize: true, - cleanStyleOnEnter: false, - enterKey: true, - - cleanOnPaste: true, - cleanSpaces: true, - pastePlainText: false, - - autosave: false, // false or url - autosaveName: false, - autosaveInterval: 60, // seconds - autosaveOnChange: false, - autosaveFields: false, - - linkTooltip: true, - linkProtocol: 'http', - linkNofollow: false, - linkSize: 50, - - imageEditable: true, - imageLink: true, - imagePosition: true, - imageFloatMargin: '10px', - imageResizable: true, - - imageUpload: null, - imageUploadParam: 'file', - - uploadImageField: false, - - dragImageUpload: true, - - fileUpload: null, - fileUploadParam: 'file', - - dragFileUpload: true, - - s3: false, - - convertLinks: true, - convertUrlLinks: true, - convertImageLinks: true, - convertVideoLinks: true, - - preSpaces: 4, // or false - tabAsSpaces: false, // true or number of spaces - tabKey: true, - - scrollTarget: false, - - toolbar: true, - toolbarFixed: true, - toolbarFixedTarget: document, - toolbarFixedTopOffset: 0, // pixels - toolbarExternal: false, // ID selector - toolbarOverflow: false, - - source: true, - buttons: ['html', 'formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', - 'outdent', 'indent', 'image', 'file', 'link', 'alignment', 'horizontalrule'], // + 'underline' - - buttonsHide: [], - buttonsHideOnMobile: [], - - formatting: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], - formattingAdd: false, - - tabifier: true, - - deniedTags: ['script', 'style'], - allowedTags: false, // or array - - paragraphizeBlocks: ['table', 'div', 'pre', 'form', 'ul', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dl', 'blockquote', 'figcaption', - 'address', 'section', 'header', 'footer', 'aside', 'article', 'object', 'style', 'script', 'iframe', 'select', 'input', 'textarea', - 'button', 'option', 'map', 'area', 'math', 'hr', 'fieldset', 'legend', 'hgroup', 'nav', 'figure', 'details', 'menu', 'summary', 'p'], - - removeComments: false, - replaceTags: [ - ['strike', 'del'], - ['b', 'strong'] - ], - replaceStyles: [ - ['font-weight:\\s?bold', "strong"], - ['font-style:\\s?italic', "em"], - ['text-decoration:\\s?underline', "u"], - ['text-decoration:\\s?line-through', 'del'] - ], - removeDataAttr: false, - - removeAttr: false, // or multi array - allowedAttr: false, // or multi array - - removeWithoutAttr: ['span'], // or false - removeEmpty: ['p'], // or false; - - activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist', - 'alignleft', 'aligncenter', 'alignright', 'justify'], - activeButtonsStates: { - b: 'bold', - strong: 'bold', - i: 'italic', - em: 'italic', - del: 'deleted', - strike: 'deleted', - ul: 'unorderedlist', - ol: 'orderedlist', - u: 'underline' - }, - - shortcuts: { - 'ctrl+shift+m, meta+shift+m': { func: 'inline.removeFormat' }, - 'ctrl+b, meta+b': { func: 'inline.format', params: ['bold'] }, - 'ctrl+i, meta+i': { func: 'inline.format', params: ['italic'] }, - 'ctrl+h, meta+h': { func: 'inline.format', params: ['superscript'] }, - 'ctrl+l, meta+l': { func: 'inline.format', params: ['subscript'] }, - 'ctrl+k, meta+k': { func: 'link.show' }, - 'ctrl+shift+7': { func: 'list.toggle', params: ['orderedlist'] }, - 'ctrl+shift+8': { func: 'list.toggle', params: ['unorderedlist'] } - }, - shortcutsAdd: false, - - // private - buffer: [], - rebuffer: [], - emptyHtml: '

', - invisibleSpace: '​', - imageTypes: ['image/png', 'image/jpeg', 'image/gif'], - indentValue: 20, - verifiedTags: ['a', 'img', 'b', 'strong', 'sub', 'sup', 'i', 'em', 'u', 'small', 'strike', 'del', 'cite', 'ul', 'ol', 'li'], // and for span tag special rule - inlineTags: ['strong', 'b', 'u', 'em', 'i', 'code', 'del', 'ins', 'samp', 'kbd', 'sup', 'sub', 'mark', 'var', 'cite', 'small'], - alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DL', 'DT', 'DD', 'DIV', 'TD', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], - blockLevelElements: ['PRE', 'UL', 'OL', 'LI'], - highContrast: false, - observe: { - dropdowns: [] - }, - - // lang - langs: { - en: { - html: 'HTML', - video: 'Insert Video', - image: 'Insert Image', - table: 'Table', - link: 'Link', - link_insert: 'Insert link', - link_edit: 'Edit link', - unlink: 'Unlink', - formatting: 'Formatting', - paragraph: 'Normal text', - quote: 'Quote', - code: 'Code', - header1: 'Header 1', - header2: 'Header 2', - header3: 'Header 3', - header4: 'Header 4', - header5: 'Header 5', - bold: 'Bold', - italic: 'Italic', - fontcolor: 'Font Color', - backcolor: 'Back Color', - unorderedlist: 'Unordered List', - orderedlist: 'Ordered List', - outdent: 'Outdent', - indent: 'Indent', - cancel: 'Cancel', - insert: 'Insert', - save: 'Save', - _delete: 'Delete', - insert_table: 'Insert Table', - insert_row_above: 'Add Row Above', - insert_row_below: 'Add Row Below', - insert_column_left: 'Add Column Left', - insert_column_right: 'Add Column Right', - delete_column: 'Delete Column', - delete_row: 'Delete Row', - delete_table: 'Delete Table', - rows: 'Rows', - columns: 'Columns', - add_head: 'Add Head', - delete_head: 'Delete Head', - title: 'Title', - image_position: 'Position', - none: 'None', - left: 'Left', - right: 'Right', - center: 'Center', - image_web_link: 'Image Web Link', - text: 'Text', - mailto: 'Email', - web: 'URL', - video_html_code: 'Video Embed Code or Youtube/Vimeo Link', - file: 'Insert File', - upload: 'Upload', - download: 'Download', - choose: 'Choose', - or_choose: 'Or choose', - drop_file_here: 'Drop file here', - align_left: 'Align text to the left', - align_center: 'Center text', - align_right: 'Align text to the right', - align_justify: 'Justify text', - horizontalrule: 'Insert Horizontal Rule', - deleted: 'Deleted', - anchor: 'Anchor', - link_new_tab: 'Open link in new tab', - underline: 'Underline', - alignment: 'Alignment', - filename: 'Name (optional)', - edit: 'Edit', - upload_label: 'Drop file here or ' - } - }, - - linkify: { - regexps: { - youtube: /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig, - vimeo: /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/, - image: /((https?|www)[^\s]+\.)(jpe?g|png|gif)(\?[^\s-]+)?/ig, - url: /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/ig, - } - }, - - codemirror: false - }; - - // Functionality - Redactor.fn = $.Redactor.prototype = { - - keyCode: { - BACKSPACE: 8, - DELETE: 46, - UP: 38, - DOWN: 40, - ENTER: 13, - SPACE: 32, - ESC: 27, - TAB: 9, - CTRL: 17, - META: 91, - SHIFT: 16, - ALT: 18, - RIGHT: 39, - LEFT: 37, - LEFT_WIN: 91 - }, - - // Initialization - init: function(el, options) - { - this.$element = $(el); - this.uuid = uuid++; + $R.lang[lang] = $R.extend($R.lang[lang], obj); +}; - // if paste event detected = true - this.rtePaste = false; - this.$pasteBox = false; +// create +$R.create = function(name) +{ + var arr = name.split('.'); + var args = [].slice.call(arguments, 1); - this.loadOptions(options); - this.loadModules(); + var type = 'classes'; + if (typeof $R.env[arr[0]] !== 'undefined') + { + type = $R.env[arr[0]]; + name = arr.slice(1).join('.'); + } - // formatting storage - this.formatting = {}; + // construct + var instance = new $R[type][name](); - // block level tags - $.merge(this.opts.blockLevelElements, this.opts.alignmentTags); - this.reIsBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i'); + // init + if (instance.init) + { + var res = instance.init.apply(instance, args); - // setup allowed and denied tags - this.tidy.setupAllowed(); + return (res) ? res : instance; + } - // setup denied tags - if (this.opts.deniedTags !== false) - { - var tags = ['html', 'head', 'link', 'body', 'meta', 'applet']; - for (var i = 0; i < tags.length; i++) - { - this.opts.deniedTags.push(tags[i]); - } - } + return instance; +}; - // load lang - this.lang.load(); +// inherit +$R.inherit = function(current, parent) +{ + var F = function () {}; + F.prototype = parent; + var f = new F(); - // extend shortcuts - $.extend(this.opts.shortcuts, this.opts.shortcutsAdd); + for (var prop in current.prototype) + { + if (current.prototype.__lookupGetter__(prop)) f.__defineGetter__(prop, current.prototype.__lookupGetter__(prop)); + else f[prop] = current.prototype[prop]; + } - // start callback - this.core.setCallback('start'); + current.prototype = f; + current.prototype.super = parent; - // build - this.start = true; - this.build.run(); - }, + return current; +}; - loadOptions: function(options) - { - this.opts = $.extend( - {}, - $.extend(true, {}, $.Redactor.opts), - this.$element.data(), - options - ); - }, - getModuleMethods: function(object) - { - return Object.getOwnPropertyNames(object).filter(function(property) - { - return typeof object[property] == 'function'; - }); - }, - loadModules: function() - { - var len = $.Redactor.modules.length; - for (var i = 0; i < len; i++) - { - this.bindModuleMethods($.Redactor.modules[i]); - } - }, - bindModuleMethods: function(module) - { - if (typeof this[module] == 'undefined') return; +// error +$R.error = function(exception) +{ + throw exception; +}; - // init module - this[module] = this[module](); +// extend +$R.extend = function() +{ + var extended = {}; + var deep = false; + var i = 0; + var length = arguments.length; + + if (Object.prototype.toString.call( arguments[0] ) === '[object Boolean]') + { + deep = arguments[0]; + i++; + } + + var merge = function(obj) + { + for (var prop in obj) + { + if (Object.prototype.hasOwnProperty.call(obj, prop)) + { + if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') extended[prop] = $R.extend(true, extended[prop], obj[prop]); + else extended[prop] = obj[prop]; + } + } + }; + + for (; i < length; i++ ) + { + var obj = arguments[i]; + merge(obj); + } + + return extended; +}; +$R.opts = { + animation: true, + lang: 'en', + direction: 'ltr', + spellcheck: true, + structure: false, + scrollTarget: false, + styles: true, + stylesClass: 'redactor-styles', + placeholder: false, + + source: true, + showSource: false, + + inline: false, + + breakline: false, + markup: 'p', + enterKey: true, + + clickToEdit: false, + clickToSave: false, + clickToCancel: false, + + focus: false, + focusEnd: false, + + minHeight: false, // string, '100px' + maxHeight: false, // string, '100px' + maxWidth: false, // string, '700px' + + plugins: [], // array + callbacks: {}, + + // pre & tab + preClass: false, // string + preSpaces: 4, // or false + tabindex: false, // int + tabAsSpaces: false, // true or number of spaces + tabKey: true, + + // autosave + autosave: false, // false or url + autosaveName: false, + autosaveData: false, + + // toolbar + toolbar: true, + toolbarFixed: true, + toolbarFixedTarget: document, + toolbarFixedTopOffset: 0, // pixels + toolbarExternal: false, // ID selector + toolbarContext: true, + + // air + air: false, + + // formatting + formatting: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + formattingAdd: false, + formattingHide: false, + + // buttons + buttons: ['html', 'format', 'bold', 'italic', 'deleted', 'lists', 'image', 'file', 'link'], + // + 'line', 'redo', 'undo', 'underline', 'ol', 'ul', 'indent', 'outdent' + buttonsTextLabeled: false, + buttonsAdd: [], + buttonsAddFirst: [], + buttonsAddAfter: false, + buttonsAddBefore: false, + buttonsHide: [], + buttonsHideOnMobile: [], + + // image + imageUpload: false, + imageUploadParam: 'file', + imageData: false, + imageEditable: true, + imageCaption: true, + imageLink: true, + imagePosition: false, + imageResizable: false, + imageFloatMargin: '10px', + imageFigure: true, + + // file + fileUpload: false, + fileUploadParam: 'file', + fileData: false, + fileAttachment: false, + + // upload opts + uploadData: false, + dragUpload: true, + multipleUpload: true, + clipboardUpload: true, + uploadBase64: false, + + // link + linkTarget: false, + linkTitle: false, + linkNewTab: false, + linkNofollow: false, + linkSize: 30, + linkValidation: true, + + // clean + cleanOnEnter: true, + cleanInlineOnEnter: false, + paragraphize: true, + removeScript: true, + removeNewLines: false, + removeComments: true, + replaceTags: { + 'b': 'strong', + 'i': 'em', + 'strike': 'del' + }, + + // paste + pastePlainText: false, + pasteLinkTarget: false, + pasteImages: true, + pasteLinks: true, + pasteClean: true, + pasteKeepStyle: [], + pasteKeepClass: [], + pasteKeepAttrs: ['td', 'th'], + pasteBlockTags: ['pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'table', 'tbody', 'thead', 'tfoot', 'th', 'tr', 'td', 'ul', 'ol', 'li', 'blockquote', 'p', 'figure', 'figcaption'], + pasteInlineTags: ['a', 'img', 'br', 'strong', 'ins', 'code', 'del', 'span', 'samp', 'kbd', 'sup', 'sub', 'mark', 'var', 'cite', 'small', 'b', 'u', 'em', 'i', 'abbr'], + + // active buttons + activeButtons: { + b: 'bold', + strong: 'bold', + i: 'italic', + em: 'italic', + del: 'deleted', + strike: 'deleted', + u: 'underline' + }, + activeButtonsAdd: {}, + activeButtonsObservers: {}, + + // autoparser + autoparse: true, + autoparseStart: true, + autoparsePaste: true, + autoparseLinks: true, + autoparseImages: true, + autoparseVideo: true, + + // shortcodes + shortcodes: { + 'p.': { format: 'p' }, + 'quote.': { format: 'blockquote' }, + 'pre.': { format: 'pre' }, + 'h1.': { format: 'h1' }, + 'h2.': { format: 'h2' }, + 'h3.': { format: 'h3' }, + 'h4.': { format: 'h4' }, + 'h5.': { format: 'h5' }, + 'h6.': { format: 'h6' }, + //'1.': { format: 'ol' }, + '*.': { format: 'ul' } + }, + shortcodesAdd: false, // object + + // shortcuts + shortcuts: { + 'ctrl+shift+m, meta+shift+m': { api: 'module.inline.clearformat' }, + 'ctrl+b, meta+b': { api: 'module.inline.format', args: 'b' }, + 'ctrl+i, meta+i': { api: 'module.inline.format', args: 'i' }, + 'ctrl+u, meta+u': { api: 'module.inline.format', args: 'u' }, + 'ctrl+h, meta+h': { api: 'module.inline.format', args: 'sup' }, + 'ctrl+l, meta+l': { api: 'module.inline.format', args: 'sub' }, + 'ctrl+k, meta+k': { api: 'module.link.open' }, + 'ctrl+alt+0, meta+alt+0': { api: 'module.block.format', args: 'p' }, + 'ctrl+alt+1, meta+alt+1': { api: 'module.block.format', args: 'h1' }, + 'ctrl+alt+2, meta+alt+2': { api: 'module.block.format', args: 'h2' }, + 'ctrl+alt+3, meta+alt+3': { api: 'module.block.format', args: 'h3' }, + 'ctrl+alt+4, meta+alt+4': { api: 'module.block.format', args: 'h4' }, + 'ctrl+alt+5, meta+alt+5': { api: 'module.block.format', args: 'h5' }, + 'ctrl+alt+6, meta+alt+6': { api: 'module.block.format', args: 'h6' }, + 'ctrl+shift+7, meta+shift+7': { api: 'module.list.toggle', args: 'ol' }, + 'ctrl+shift+8, meta+shift+8': { api: 'module.list.toggle', args: 'ul' } + }, + shortcutsAdd: false, // object + + // misc + grammarly: true, + notranslate: false, + + // private + bufferLimit: 100, + emptyHtml: '

', + markerChar: '\ufeff', + imageTypes: ['image/png', 'image/jpeg', 'image/gif'], + inlineTags: ['a', 'span', 'strong', 'strike', 'b', 'u', 'em', 'i', 'code', 'del', 'ins', 'samp', 'kbd', 'sup', 'sub', 'mark', 'var', 'cite', 'small', 'abbr'], + blockTags: ['pre', 'ul', 'ol', 'li', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dl', 'dt', 'dd', 'div', 'table', 'tbody', 'thead', 'tfoot', 'tr', 'th', 'td', 'blockquote', 'output', 'figcaption', 'figure', 'address', 'section', 'header', 'footer', 'aside', 'article', 'iframe'], + regex: { + youtube: /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/gi, + vimeo: /(http|https)?:\/\/(?:www.|player.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|video\/|)(\d+)(?:[a-zA-Z0-9_-]+)?/gi, + imageurl: /((https?|www)[^\s]+\.)(jpe?g|png|gif)(\?[^\s-]+)?/gi, + url: /(https?:\/\/(?:www\.|(?!www))[^\s\.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/gi + }, + input: true, + zindex: false, + modes: { + "inline": { + pastePlainText: true, + pasteImages: false, + enterKey: false, + toolbar: false, + autoparse: false, + source: false, + showSource: false, + styles: false, + air: false + }, + "original": { + styles: false + } + } +}; +$R.lang['en'] = { + "format": "Format", + "image": "Image", + "file": "File", + "link": "Link", + "bold": "Bold", + "italic": "Italic", + "deleted": "Strikethrough", + "underline": "Underline", + "superscript": "Superscript", + "subscript": "Subscript", + "bold-abbr": "B", + "italic-abbr": "I", + "deleted-abbr": "S", + "underline-abbr": "U", + "superscript-abbr": "Sup", + "subscript-abbr": "Sub", + "lists": "Lists", + "link-insert": "Insert Link", + "link-edit": "Edit Link", + "link-in-new-tab": "Open link in new tab", + "unlink": "Unlink", + "cancel": "Cancel", + "close": "Close", + "insert": "Insert", + "save": "Save", + "delete": "Delete", + "text": "Text", + "edit": "Edit", + "title": "Alt", + "paragraph": "Normal text", + "quote": "Quote", + "code": "Code", + "heading1": "Heading 1", + "heading2": "Heading 2", + "heading3": "Heading 3", + "heading4": "Heading 4", + "heading5": "Heading 5", + "heading6": "Heading 6", + "filename": "Name", + "optional": "optional", + "unorderedlist": "Unordered List", + "orderedlist": "Ordered List", + "outdent": "Outdent", + "indent": "Indent", + "horizontalrule": "Line", + "upload": "Upload", + "upload-label": "Drop files here or click to upload", + "accessibility-help-label": "Rich text editor", + "caption": "Caption", + "bulletslist": "Bullets", + "numberslist": "Numbers", + "image-position": "Position", + "none": "None", + "left": "Left", + "right": "Right", + "center": "Center", + "undo": "Undo", + "redo": "Redo" +}; +$R.buttons = { + html: { + title: 'HTML', + icon: true, + api: 'module.source.toggle' + }, + undo: { + title: '## undo ##', + icon: true, + api: 'module.buffer.undo' + }, + redo: { + title: '## redo ##', + icon: true, + api: 'module.buffer.redo' + }, + format: { + title: '## format ##', + icon: true, + dropdown: { + p: { + title: '## paragraph ##', + api: 'module.block.format', + args: { + tag: 'p' + } + }, + blockquote: { + title: '## quote ##', + api: 'module.block.format', + args: { + tag: 'blockquote' + } + }, + pre: { + title: '## code ##', + api: 'module.block.format', + args: { + tag: 'pre' + } + }, + h1: { + title: '## heading1 ##', + api: 'module.block.format', + args: { + tag: 'h1' + } + }, + h2: { + title: '## heading2 ##', + api: 'module.block.format', + args: { + tag: 'h2' + } + }, + h3: { + title: '## heading3 ##', + api: 'module.block.format', + args: { + tag: 'h3' + } + }, + h4: { + title: '## heading4 ##', + api: 'module.block.format', + args: { + tag: 'h4' + } + }, + h5: { + title: '## heading5 ##', + api: 'module.block.format', + args: { + tag: 'h5' + } + }, + h6: { + title: '## heading6 ##', + api: 'module.block.format', + args: { + tag: 'h6' + } + } + } + }, + bold: { + title: '## bold-abbr ##', + icon: true, + tooltip: '## bold ##', + api: 'module.inline.format', + args: { + tag: 'b' + } + }, + italic: { + title: '## italic-abbr ##', + icon: true, + tooltip: '## italic ##', + api: 'module.inline.format', + args: { + tag: 'i' + } + }, + deleted: { + title: '## deleted-abbr ##', + icon: true, + tooltip: '## deleted ##', + api: 'module.inline.format', + args: { + tag: 'del' + } + }, + underline: { + title: '## underline-abbr ##', + icon: true, + tooltip: '## underline ##', + api: 'module.inline.format', + args: { + tag: 'u' + } + }, + sup: { + title: '## superscript-abbr ##', + icon: true, + tooltip: '## superscript ##', + api: 'module.inline.format', + args: { + tag: 'sup' + } + }, + sub: { + title: '## subscript-abbr ##', + icon: true, + tooltip: '## subscript ##', + api: 'module.inline.format', + args: { + tag: 'sub' + } + }, + lists: { + title: '## lists ##', + icon: true, + observe: 'list', + dropdown: { + observe: 'list', + unorderedlist: { + title: '• ## unorderedlist ##', + api: 'module.list.toggle', + args: 'ul' + }, + orderedlist: { + title: '1. ## orderedlist ##', + api: 'module.list.toggle', + args: 'ol' + }, + outdent: { + title: '< ## outdent ##', + api: 'module.list.outdent' + }, + indent: { + title: '> ## indent ##', + api: 'module.list.indent' + } + } + }, + ul: { + title: '• ## bulletslist ##', + icon: true, + api: 'module.list.toggle', + observe: 'list', + args: 'ul' + }, + ol: { + title: '1. ## numberslist ##', + icon: true, + api: 'module.list.toggle', + observe: 'list', + args: 'ol' + }, + outdent: { + title: '## outdent ##', + icon: true, + api: 'module.list.outdent', + observe: 'list' + }, + indent: { + title: '## indent ##', + icon: true, + api: 'module.list.indent', + observe: 'list' + }, + image: { + title: '## image ##', + icon: true, + api: 'module.image.open' + }, + file: { + title: '## file ##', + icon: true, + api: 'module.file.open' + }, + link: { + title: '## link ##', + icon: true, + observe: 'link', + dropdown: { + observe: 'link', + link: { + title: '## link-insert ##', + api: 'module.link.open' + }, + unlink: { + title: '## unlink ##', + api: 'module.link.unlink' + } + } + }, + line: { + title: '## horizontalrule ##', + icon: true, + api: 'module.line.insert' + } +}; +var App = function(element, options, uuid) +{ + this.module = {}; + this.plugin = {}; + this.instances = {}; + + // start/stop + this.started = false; + this.stopped = false; + + // environment + this.uuid = uuid; + this.rootElement = element; + this.rootOpts = options; + this.dragInside = false; + this.dragComponentInside = false; + this.keycodes = $R.keycodes; + this.namespace = 'redactor'; + this.$win = $R.dom(window); + this.$doc = $R.dom(document); + this.$body = $R.dom('body'); + this.editorReadOnly = false; + + // core services + this.opts = $R.create('service.options', options, element); + this.lang = $R.create('service.lang', this); + + // build + this.buildServices(); + this.buildModules(); + this.buildPlugins(); + + // start + this.start(); +}; + +App.prototype = { + start: function() + { + // start + this.stopped = false; + this.broadcast('start'); + this.broadcast('startcode'); + + if (this.opts.clickToEdit) + { + this.broadcast('startclicktoedit'); + } + else + { + this.broadcast('enable'); + if (this.opts.showSource) this.broadcast('startcodeshow'); + this.broadcast('enablefocus'); + } + + // started + this.broadcast('started'); + this.started = true; + }, + stop: function() + { + this.started = false; + this.stopped = true; + + this.broadcast('stop'); + this.broadcast('disable'); + this.broadcast('stopped'); + }, + + // started & stopped + isStarted: function() + { + return this.started; + }, + isStopped: function() + { + return this.stopped; + }, + + // build + buildServices: function() + { + var core = ['options', 'lang']; + var bindable = ['uuid', 'keycodes', 'opts', 'lang', '$win', '$doc', '$body']; + var services = []; + for (var name in $R.services) + { + if (core.indexOf(name) === -1) + { + this[name] = $R.create('service.' + name, this); + services.push(name); + bindable.push(name); + } + } + + // binding + for (var i = 0; i < services.length; i++) + { + var service = services[i]; + for (var z = 0; z < bindable.length; z++) + { + var inj = bindable[z]; + if (service !== inj) + { + this[service][inj] = this[inj]; + } + } + } + }, + buildModules: function() + { + for (var name in $R.modules) + { + this.module[name] = $R.create('module.' + name, this); + this.instances[name] = this.module[name]; + } + }, + buildPlugins: function() + { + var plugins = this.opts.plugins; + for (var i = 0; i < plugins.length; i++) + { + var name = plugins[i]; + if (typeof $R.plugins[name] !== 'undefined') + { + this.plugin[name] = $R.create('plugin.' + name, this); + this.instances[name] = this.plugin[name]; + } + } + }, + + // draginside + isDragInside: function() + { + return this.dragInside; + }, + setDragInside: function(dragInside) + { + this.dragInside = dragInside; + }, + isDragComponentInside: function() + { + return this.dragComponentInside; + }, + setDragComponentInside: function(dragInside) + { + this.dragComponentInside = dragInside; + }, + getDragComponentInside: function() + { + return this.dragComponentInside; + }, + + // readonly + isReadOnly: function() + { + return this.editorReadOnly; + }, + enableReadOnly: function() + { + this.editorReadOnly = true; + this.broadcast('enablereadonly'); + this.component.clearActive(); + this.toolbar.disableButtons(); + }, + disableReadOnly: function() + { + this.editorReadOnly = false; + this.broadcast('disablereadonly'); + this.toolbar.enableButtons(); + }, + + // messaging + callMessageHandler: function(instance, name, args) + { + var arr = name.split('.'); + if (arr.length === 1) + { + if (typeof instance['on' + name] === 'function') + { + instance['on' + name].apply(instance, args); + } + } + else + { + arr[0] = 'on' + arr[0]; + + var func = this.utils.checkProperty(instance, arr); + if (typeof func === 'function') + { + func.apply(instance, args); + } + } + }, + broadcast: function(name) + { + var args = [].slice.call(arguments, 1); + for (var moduleName in this.instances) + { + this.callMessageHandler(this.instances[moduleName], name, args); + } + + // callback + return this.callback.trigger(name, args); + }, + + // callback + on: function(name, func) + { + this.callback.add(name, func); + }, + off: function(name, func) + { + this.callback.remove(name, func); + }, + + // api + api: function(name) + { + if (!this.isStarted() && name !== 'start') return; + if (this.isReadOnly() && name !== 'disableReadOnly') return; + + this.broadcast('state'); + + var args = [].slice.call(arguments, 1); + var arr = name.split('.'); + + var isApp = (arr.length === 1); + var isCallback = (arr[0] === 'on' || arr[0] === 'off'); + var isService = (!isCallback && arr.length === 2); + var isPlugin = (arr[0] === 'plugin'); + var isModule = (arr[0] === 'module'); + + // app + if (isApp) + { + if (typeof this[arr[0]] === 'function') + { + return this.callInstanceMethod(this, arr[0], args); + } + } + // callback + else if (isCallback) + { + return (arr[0] === 'on') ? this.on(arr[1], args[0]) : this.off(arr[1], args[0] || undefined); + } + // service + else if (isService) + { + if (this.isInstanceExists(this, arr[0])) + { + return this.callInstanceMethod(this[arr[0]], arr[1], args); + } + else + { + $R.error(new Error('Service "' + arr[0] + '" not found')); + } + } + // plugin + else if (isPlugin) + { + if (this.isInstanceExists(this.plugin, arr[1])) + { + return this.callInstanceMethod(this.plugin[arr[1]], arr[2], args); + } + else + { + $R.error(new Error('Plugin "' + arr[1] + '" not found')); + } + } + // module + else if (isModule) + { + if (this.isInstanceExists(this.module, arr[1])) + { + return this.callInstanceMethod(this.module[arr[1]], arr[2], args); + } + else + { + $R.error(new Error('Module "' + arr[1] + '" not found')); + } + } + + }, + isInstanceExists: function(obj, name) + { + return (typeof obj[name] !== 'undefined'); + }, + callInstanceMethod: function(instance, method, args) + { + if (typeof instance[method] === 'function') + { + return instance[method].apply(instance, args); + } + } +}; +$R.add('mixin', 'formatter', { + + // public + buildArgs: function(args) + { + this.args = { + 'class': args['class'] || false, + 'style': args['style'] || false, + 'attr': args['attr'] || false + }; + + if (!this.args['class'] && !this.args['style'] && !this.args['attr']) + { + this.args = false; + } + }, + applyArgs: function(nodes, selection) + { + if (this.args) + { + nodes = this[this.type](this.args, false, nodes, selection); + } + else + { + nodes = this._clearAll(nodes, selection); + } + + return nodes; + }, + clearClass: function(tags, nodes) + { + this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags, true); + $elements.removeAttr('class'); + + nodes = this._unwrapSpanWithoutAttr($elements.getAll()); + + this.selection.restore(); + + return nodes; + }, + clearStyle: function(tags, nodes) + { + this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags, true); + $elements.removeAttr('style'); + + nodes = this._unwrapSpanWithoutAttr($elements.getAll()); + + this.selection.restore(); + + return nodes; + }, + clearAttr: function(tags, nodes) + { + this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags, true); + this._removeAllAttr($elements); + + nodes = this._unwrapSpanWithoutAttr($elements.getAll()); + + this.selection.restore(); + + return nodes; + }, + set: function(args, tags, nodes, selection) + { + if (selection !== false) this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags); + + if (args['class']) + { + $elements.removeAttr('class'); + $elements.addClass(args['class']); + } + + if (args['style']) + { + $elements.removeAttr('style'); + $elements.css(args['style']); + $elements.each(function(node) + { + var $node = $R.dom(node); + $node.attr('data-redactor-style-cache', $node.attr('style')); + }); + } + + if (args['attr']) + { + this._removeAllAttr($elements); + $elements.attr(args['attr']); + } + + if (selection !== false) this.selection.restore(); + + return $elements.getAll(); + }, + toggle: function(args, tags, nodes, selection) + { + if (selection !== false) this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags); + + if (args['class']) + { + $elements.toggleClass(args['class']); + $elements.each(function(node) + { + if (node.className === '') node.removeAttribute('class'); + }); + } + + var params; + if (args['style']) + { + params = args['style']; + $elements.each(function(node) + { + var $node = $R.dom(node); + for (var key in params) + { + var newVal = params[key]; + var oldVal = $node.css(key); + + oldVal = (this.utils.isRgb(oldVal)) ? this.utils.rgb2hex(oldVal) : oldVal.replace(/"/g, ''); + newVal = (this.utils.isRgb(newVal)) ? this.utils.rgb2hex(newVal) : newVal.replace(/"/g, ''); + + oldVal = this.utils.hex2long(oldVal); + newVal = this.utils.hex2long(newVal); + + var compareNew = (typeof newVal === 'string') ? newVal.toLowerCase() : newVal; + var compareOld = (typeof oldVal === 'string') ? oldVal.toLowerCase() : oldVal; + + if (compareNew === compareOld) $node.css(key, ''); + else $node.css(key, newVal); + } + + this._convertStyleQuotes($node); + if (this.utils.removeEmptyAttr(node, 'style')) + { + $node.removeAttr('data-redactor-style-cache'); + } + else + { + $node.attr('data-redactor-style-cache', $node.attr('style')); + } + + }.bind(this)); + } + + if (args['attr']) + { + params = args['attr']; + $elements.each(function(node) + { + var $node = $R.dom(node); + for (var key in params) + { + if ($node.attr(key)) $node.removeAttr(key); + else $node.attr(key, params[key]); + } + }); + + } + + if (selection !== false) this.selection.restore(); + + return $elements.getAll(); + }, + add: function(args, tags, nodes, selection) + { + if (selection !== false) this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags); + + if (args['class']) + { + $elements.addClass(args['class']); + } + + if (args['style']) + { + var params = args['style']; + $elements.each(function(node) + { + var $node = $R.dom(node); + $node.css(params); + $node.attr('data-redactor-style-cache', $node.attr('style')); + + this._convertStyleQuotes($node); + + }.bind(this)); + } + + if (args['attr']) + { + $elements.attr(args['attr']); + } + + if (selection !== false) this.selection.restore(); + + return $elements.getAll(); + }, + remove: function(args, tags, nodes, selection) + { + if (selection !== false) this.selection.save(); + + var $elements = (nodes) ? $R.dom(nodes) : this.getElements(tags); + + if (args['class']) + { + $elements.removeClass(args['class']); + $elements.each(function(node) + { + if (node.className === '') node.removeAttribute('class'); + }); + } + + if (args['style']) + { + var name = args['style']; + $elements.each(function(node) + { + var $node = $R.dom(node); + $node.css(name, ''); + + if (this.utils.removeEmptyAttr(node, 'style')) + { + $node.removeAttr('data-redactor-style-cache'); + } + else + { + $node.attr('data-redactor-style-cache', $node.attr('style')); + } + + }.bind(this)); + } + + if (args['attr']) + { + $elements.removeAttr(args['attr']); + } + + nodes = this._unwrapSpanWithoutAttr($elements.getAll()); + + if (selection !== false) this.selection.restore(); + + return nodes; + }, + + // private + _removeAllAttr: function($elements) + { + $elements.each(function(node) + { + for (var i = node.attributes.length; i-->0;) + { + var nodeAttr = node.attributes[i]; + var name = nodeAttr.name; + if (name !== 'style' && name !== 'class') + { + node.removeAttributeNode(nodeAttr); + } + } + }); + }, + _convertStyleQuotes: function($node) + { + var style = $node.attr('style'); + if (style) $node.attr('style', style.replace(/"/g, '\'')); + }, + _clearAll: function(nodes, selection) + { + if (selection !== false) this.selection.save(); + + for (var i = 0; i < nodes.length; i++) + { + var node = nodes[i]; + while (node.attributes.length > 0) + { + node.removeAttribute(node.attributes[0].name); + } + } + + nodes = this._unwrapSpanWithoutAttr(nodes); + + if (selection !== false) this.selection.restore(); + + return nodes; + }, + _unwrapSpanWithoutAttr: function(nodes) + { + var finalNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var node = nodes[i]; + var len = node.attributes.length; + if (len <= 0 && node.nodeType !== 3 && node.tagName === 'SPAN') + { + $R.dom(node).unwrap(); + } + else + { + finalNodes.push(node); + } + } + + return finalNodes; + } +}); +$R.add('mixin', 'dom', $R.Dom.prototype); +$R.add('mixin', 'component', { + get cmnt() + { + return true; + } +}); +$R.add('service', 'options', { + init: function(options, element) + { + var $el = $R.dom(element); + var opts = $R.extend({}, $R.opts, (element) ? $el.data() : {}, $R.options); + opts = $R.extend(true, opts, options); + + return opts; + } +}); +$R.add('service', 'lang', { + init: function(app) + { + this.app = app; + this.opts = app.opts; + + // build + this.vars = this._build(this.opts.lang); + }, + + // public + rebuild: function(lang) + { + this.opts.lang = lang; + this.vars = this._build(lang); + }, + extend: function(obj) + { + this.vars = $R.extend(this.vars, obj); + }, + parse: function(str) + { + if (str === undefined) + { + return ''; + } + + var matches = str.match(/## (.*?) ##/g); + if (matches) + { + for (var i = 0; i < matches.length; i++) + { + var key = matches[i].replace(/^##\s/g, '').replace(/\s##$/g, ''); + str = str.replace(matches[i], this.get(key)); + } + } + + return str; + }, + get: function(name) + { + var str = ''; + if (typeof this.vars[name] !== 'undefined') + { + str = this.vars[name]; + } + else if (this.opts.lang !== 'en' && typeof $R.lang['en'][name] !== 'undefined') + { + str = $R.lang['en'][name]; + } + + return str; + }, + + // private + _build: function(lang) + { + var vars = $R.lang['en']; + if (lang !== 'en') + { + vars = ($R.lang[lang] !== undefined) ? $R.lang[lang] : vars; + } + + return vars; + } +}); +$R.add('service', 'callback', { + init: function(app) + { + this.app = app; + this.opts = app.opts; + + // local + this.callbacks = {}; + + // build + if (this.opts.callbacks) + { + this._set(this.opts.callbacks, ''); + } + }, + stop: function() + { + this.callbacks = {}; + }, + add: function(name, handler) + { + if (!this.callbacks[name]) this.callbacks[name] = []; + this.callbacks[name].push(handler); + }, + remove: function(name, handler) + { + if (handler === undefined) + { + delete this.callbacks[name]; + } + else + { + for (var i = 0; i < this.callbacks[name].length; i++) + { + this.callbacks[name].splice(i, 1); + } + + if (Object.keys(this.callbacks[name]).length === 0) delete this.callbacks[name]; + } + }, + trigger: function(name, args) + { + var value = this._loop(name, args, this.callbacks); + return (typeof value === 'undefined' && args && args[0] !== false) ? args[0] : value; + }, + + // private + _set: function(obj, name) + { + for (var key in obj) + { + var path = (name === '') ? key : name + '.' + key; + if (typeof obj[key] === 'object') + { + this._set(obj[key], path); + } + else + { + this.callbacks[path] = []; + this.callbacks[path].push(obj[key]); + } + } + }, + _loop: function(name, args, obj) + { + var value; + for (var key in obj) + { + if (name === key) + { + for (var i = 0; i < obj[key].length; i++) + { + value = obj[key][i].apply(this.app, args); + } + } + } + + return value; + } +}); +$R.add('service', 'animate', { + init: function(app) + { + this.animationOpt = app.opts.animation; + }, + start: function(element, animation, options, callback) + { + var defaults = { + duration: false, + iterate: false, + delay: false, + timing: false, + prefix: 'redactor-' + }; + + defaults = (typeof options === 'function') ? defaults : $R.extend(defaults, options); + callback = (typeof options === 'function') ? options : callback; + + // play + return new $R.AnimatePlay(element, animation, defaults, callback, this.animationOpt); + }, + stop: function(element) + { + this.$el = $R.dom(element); + this.$el.removeClass('redactor-animated'); + + var effect = this.$el.attr('redactor-animate-effect'); + this.$el.removeClass(effect); + + this.$el.removeAttr('redactor-animate-effect'); + var hide = this.$el.attr('redactor-animate-hide'); + if (hide) + { + this.$el.addClass(hide).removeAttr('redactor-animate-hide'); + } + + this.$el.off('animationend webkitAnimationEnd'); + } +}); + +$R.AnimatePlay = function(element, animation, defaults, callback, animationOpt) +{ + this.hidableEffects = ['fadeOut', 'flipOut', 'slideUp', 'zoomOut', 'slideOutUp', 'slideOutRight', 'slideOutLeft']; + this.prefixes = ['', '-webkit-']; + + this.$el = $R.dom(element); + this.$body = $R.dom('body'); + this.callback = callback; + this.animation = (!animationOpt) ? this.buildAnimationOff(animation) : animation; + this.defaults = defaults; + + if (this.animation === 'slideUp') + { + this.$el.height(this.$el.height()); + } + + // animate + return (this.isInanimate()) ? this.inanimate() : this.animate(); +}; + +$R.AnimatePlay.prototype = { + buildAnimationOff: function(animation) + { + return (this.isHidable(animation)) ? 'hide' : 'show'; + }, + buildHideClass: function() + { + return 'redactor-animate-hide'; + }, + isInanimate: function() + { + return (this.animation === 'show' || this.animation === 'hide'); + }, + isAnimated: function() + { + return this.$el.hasClass('redactor-animated'); + }, + isHidable: function(effect) + { + return (this.hidableEffects.indexOf(effect) !== -1); + }, + inanimate: function() + { + this.defaults.timing = 'linear'; + + var hide; + if (this.animation === 'show') + { + hide = this.buildHideClass(); + this.$el.attr('redactor-animate-hide', hide); + this.$el.removeClass(hide); + } + else + { + hide = this.$el.attr('redactor-animate-hide'); + this.$el.addClass(hide).removeAttr('redactor-animate-hide'); + } + + if (typeof this.callback === 'function') this.callback(this); + + return this; + }, + animate: function() + { + var delay = (this.defaults.delay) ? this.defaults.delay : 0; + setTimeout(function() + { + this.$body.addClass('no-scroll-x'); + this.$el.addClass('redactor-animated'); + if (!this.$el.attr('redactor-animate-hide')) + { + var hide = this.buildHideClass(); + this.$el.attr('redactor-animate-hide', hide); + this.$el.removeClass(hide); + } + + this.$el.addClass(this.defaults.prefix + this.animation); + this.$el.attr('redactor-animate-effect', this.defaults.prefix + this.animation); + + this.set(this.defaults.duration + 's', this.defaults.iterate, this.defaults.timing); + this.complete(); + + }.bind(this), delay * 1000); + + return this; + }, + set: function(duration, iterate, timing) + { + var len = this.prefixes.length; + + while (len--) + { + if (duration !== false || duration === '') this.$el.css(this.prefixes[len] + 'animation-duration', duration); + if (iterate !== false || iterate === '') this.$el.css(this.prefixes[len] + 'animation-iteration-count', iterate); + if (timing !== false || timing === '') this.$el.css(this.prefixes[len] + 'animation-timing-function', timing); + } + }, + clean: function() + { + this.$body.removeClass('no-scroll-x'); + this.$el.removeClass('redactor-animated'); + this.$el.removeClass(this.defaults.prefix + this.animation); + this.$el.removeAttr('redactor-animate-effect'); + + this.set('', '', ''); + }, + complete: function() + { + this.$el.one('animationend webkitAnimationEnd', function() + { + if (this.$el.hasClass(this.defaults.prefix + this.animation)) this.clean(); + if (this.isHidable(this.animation)) + { + var hide = this.$el.attr('redactor-animate-hide'); + this.$el.addClass(hide).removeAttr('redactor-animate-hide'); + } + + if (this.animation === 'slideUp') this.$el.height(''); + if (typeof this.callback === 'function') this.callback(this.$el); + + }.bind(this)); + } +}; +$R.add('service', 'caret', { + init: function(app) + { + this.app = app; + }, + + // set + setStart: function(el) + { + this._setCaret('Start', el); + }, + setEnd: function(el) + { + this._setCaret('End', el); + }, + setBefore: function(el) + { + this._setCaret('Before', el); + }, + setAfter: function(el) + { + this._setCaret('After', el); + }, + + // is + isStart: function(el) + { + return this._isStartOrEnd(el, 'First'); + }, + isEnd: function(el) + { + return this._isStartOrEnd(el, 'Last'); + }, + + // set side + setAtEnd: function(node) + { + var data = this.inspector.parse(node); + var tag = data.getTag(); + var range = document.createRange(); + if (this._isInPage(node)) + { + if (tag === 'a') + { + var textNode = this.utils.createInvisibleChar(); + $R.dom(node).after(textNode); + + range.selectNodeContents(textNode); + range.collapse(true); + } + else + { + range.selectNodeContents(node); + range.collapse(false); + } + + this.selection.setRange(range); + } + }, + setAtStart: function(node) + { + var range = document.createRange(); + var data = this.inspector.parse(node); + if (this._isInPage(node)) + { + range.setStart(node, 0); + range.collapse(true); + + if (data.isInline() || this.utils.isEmpty(node)) + { + var textNode = this.utils.createInvisibleChar(); + range.insertNode(textNode); + range.selectNodeContents(textNode); + range.collapse(false); + } + + this.selection.setRange(range); + } + }, + setAtBefore: function(node) + { + var data = this.inspector.parse(node); + var range = document.createRange(); + if (this._isInPage(node)) + { + range.setStartBefore(node); + range.collapse(true); + + if (data.isInline()) + { + var textNode = this.utils.createInvisibleChar(); + node.parentNode.insertBefore(textNode, node); + range.selectNodeContents(textNode); + range.collapse(false); + } + + this.selection.setRange(range); + } + }, + setAtAfter: function(node) + { + + var range = document.createRange(); + if (this._isInPage(node)) + { + range.setStartAfter(node); + range.collapse(true); + + var textNode = this.utils.createInvisibleChar(); + range.insertNode(textNode); + range.selectNodeContents(textNode); + range.collapse(false); + + this.selection.setRange(range); + } + }, + setAtPrev: function(node) + { + var prev = node.previousSibling; + if (prev) + { + prev = (prev.nodeType === 3 && this._isEmptyTextNode(prev)) ? prev.previousElementSibling : prev; + if (prev) this.setEnd(prev); + } + }, + setAtNext: function(node) + { + var next = node.nextSibling; + if (next) + { + next = (next.nodeType === 3 && this._isEmptyTextNode(next)) ? next.nextElementSibling : next; + if (next) this.setStart(next); + } + }, + + // private + _setCaret: function(type, el) + { + var data = this.inspector.parse(el); + var node = data.getNode(); + + if (node) + { + this.component.clearActive(); + this['_set' + type](node, data, data.getTag()); + } + }, + _setStart: function(node, data, tag) + { + // 1. text + if (data.isText()) + { + this.editor.focus(); + return this.setAtStart(node); + } + // 2. ul, ol + else if (tag === 'ul' || tag === 'ol') + { + node = data.findFirstNode('li'); + + var item = this.utils.getFirstElement(node); + var dataItem = this.inspector.parse(item); + if (item && dataItem.isComponent()) + { + return this.setStart(dataItem.getComponent()); + } + } + // 3. dl + else if (tag === 'dl') + { + node = data.findFirstNode('dt'); + } + // 4. br / hr + else if (tag === 'br' || tag === 'hr') + { + return this.setBefore(node); + } + // 5. th, td + else if (tag === 'td' || tag === 'th') + { + var el = data.getFirstElement(node); + if (el) + { + return this.setStart(el); + } + } + // 6. table + else if (tag === 'table' || tag === 'tr') + { + return this.setStart(data.findFirstNode('th, td')); + } + // 7. figure code + else if (data.isComponentType('code') && !data.isFigcaption()) + { + var code = data.findLastNode('pre, code'); + + this.editor.focus(); + return this.setAtStart(code); + } + // 8. table component + else if (tag === 'figure' && data.isComponentType('table')) + { + var table = data.getTable(); + var tableData = this.inspector.parse(table); + + return this.setStart(tableData.findFirstNode('th, td')); + } + // 9. non editable components + else if (!data.isComponentType('table') && data.isComponent() && !data.isFigcaption()) + { + return this.component.setActive(node); + } + + this.editor.focus(); + + // set + if (!this._setInline(node, 'Start')) + { + this.setAtStart(node); + } + }, + _setEnd: function(node, data, tag) + { + // 1. text + if (data.isText()) + { + this.editor.focus(); + return this.setAtEnd(node); + } + // 2. ul, ol + else if (tag === 'ul' || tag === 'ol') + { + node = data.findLastNode('li'); + + var item = this.utils.getLastElement(node); + var dataItem = this.inspector.parse(item); + if (item && dataItem.isComponent()) + { + return this.setEnd(dataItem.getComponent()); + } + } + // 3. dl + else if (tag === 'dl') + { + node = data.findLastNode('dd'); + } + // 4. br / hr + else if (tag === 'br' || tag === 'hr') + { + return this.setAfter(node); + } + // 5. th, td + else if (tag === 'td' || tag === 'th') + { + var el = data.getLastElement(); + if (el) + { + return this.setEnd(el); + } + } + // 6. table + else if (tag === 'table' || tag === 'tr') + { + return this.setEnd(data.findLastNode('th, td')); + } + // 7. figure code + else if (data.isComponentType('code') && !data.isFigcaption()) + { + var code = data.findLastNode('pre, code'); + + this.editor.focus(); + return this.setAtEnd(code); + } + // 8. table component + else if (tag === 'figure' && data.isComponentType('table')) + { + var table = data.getTable(); + var tableData = this.inspector.parse(table); + + return this.setEnd(tableData.findLastNode('th, td')); + } + // 9. non editable components + else if (!data.isComponentType('table') && data.isComponent() && !data.isFigcaption()) + { + return this.component.setActive(node); + } + + this.editor.focus(); + + // set + if (!this._setInline(node, 'End')) + { + // is element empty + if (this.utils.isEmpty(node)) + { + return this.setStart(node); + } + + this.setAtEnd(node); + } + }, + _setBefore: function(node, data, tag) + { + // text + if (node.nodeType === 3) + { + return this.setAtBefore(node); + } + // inline + else if (data.isInline()) + { + return this.setAtBefore(node); + } + // td / th + else if (data.isFirstTableCell()) + { + return this.setAtPrev(data.getComponent()); + } + else if (tag === 'td' || tag === 'th') + { + return this.setAtPrev(node); + } + // li + else if (data.isFirstListItem()) + { + return this.setAtPrev(data.getList()); + } + // figcaption + else if (data.isFigcaption()) + { + return this.setStart(data.getComponent()); + } + // component + else if (!data.isComponentType('table') && data.isComponent()) + { + return this.setAtPrev(data.getComponent()); + } + // block + else if (data.isBlock()) + { + return this.setAtPrev(node); + } + + this.editor.focus(); + this.setAtBefore(node); + + }, + _setAfter: function(node, data, tag) + { + // text + if (node.nodeType === 3) + { + return this.setAtAfter(node); + } + // inline + else if (data.isInline()) + { + return this.setAtAfter(node); + } + // td / th + else if (data.isLastTableCell()) + { + return this.setAtNext(data.getComponent()); + } + else if (tag === 'td' || tag === 'th') + { + return this.setAtNext(node); + } + // li + else if (data.isFirstListItem()) + { + return this.setAtNext(data.getList()); + } + // component + else if (!data.isComponentType('table') && data.isComponent()) + { + return this.setAtNext(data.getComponent()); + } + // block + else if (data.isBlock()) + { + return this.setAtNext(node); + } + + this.editor.focus(); + this.setAtAfter(node); + }, + _setInline: function(node, type) + { + // is first element inline (FF only) + var inline = this._hasInlineChild(node, (type === 'Start') ? 'first' : 'last'); + if (inline) + { + if (type === 'Start') + { + this.setStart(inline); + } + else + { + this.setEnd(inline); + } + + return true; + } + }, + _isStartOrEnd: function(el, type) + { + var node = this.utils.getNode(el); + if (!node) return false; + + var data = this.inspector.parse(node); + node = this._getStartEndNode(node, data, type); + + if (node && (node.nodeType !== 3 && node.tagName !== 'LI')) + { + var html = (node.nodeType === 3) ? node.textContent : node.innerHTML; + html = this.utils.trimSpaces(html); + if (html === '') return true; + } + + if (!data.isFigcaption() && data.isComponent() && !data.isComponentEditable()) + { + return true; + } + + var offset = this.offset.get(node, true); + if (offset) + { + return (type === 'First') ? (offset.start === 0) : (offset.end === this.offset.size(node, true)); + } + else + { + return false; + } + }, + _isInPage: function(node) + { + if (node && node.nodeType) + { + return (node === document.body) ? false : document.body.contains(node); + } + else + { + return false; + } + }, + _hasInlineChild: function(el, pos) + { + var data = this.inspector.parse(el); + var node = (pos === 'first') ? data.getFirstNode() : data.getLastNode(); + var $node = $R.dom(node); + + if (node && node.nodeType !== 3 + && this.inspector.isInlineTag(node.tagName) + && !$node.hasClass('redactor-component') + && !$node.hasClass('non-editable')) + { + return node; + } + }, + _isEmptyTextNode: function(node) + { + var text = node.textContent.trim().replace(/\n/, ''); + text = this.utils.removeInvisibleChars(text); + + return (text === ''); + }, + _getStartEndNode: function(node, data, type) + { + if (data.isFigcaption()) + { + node = data.getFigcaption(); + } + else if (data.isTable()) + { + node = data['find' + type + 'Node']('th, td'); + } + else if (data.isList()) + { + node = data['find' + type + 'Node']('li'); + } + else if (data.isComponentType('code')) + { + node = data.findLastNode('pre, code'); + } + + return node; + } +}); +$R.add('service', 'selection', { + init: function(app) + { + this.app = app; + }, + // is + is: function() + { + var sel = this.get(); + if (sel) + { + var node = sel.anchorNode; + var data = this.inspector.parse(node); + + return (data.isInEditor() || data.isEditor()); + } + + return false; + }, + isCollapsed: function() + { + var sel = this.get(); + var range = this.getRange(); + + if (sel && sel.isCollapsed) return true; + else if (range && range.toString().length === 0) return true; + + return false; + }, + isBackwards: function() + { + var backwards = false; + var sel = this.get(); + + if (sel && !sel.isCollapsed) + { + var range = document.createRange(); + range.setStart(sel.anchorNode, sel.anchorOffset); + range.setEnd(sel.focusNode, sel.focusOffset); + backwards = range.collapsed; + range.detach(); + } + + return backwards; + }, + isIn: function(el) + { + var node = $R.dom(el).get(); + var current = this.getCurrent(); + + return (current && node) ? node.contains(current) : false; + }, + isText: function() + { + var sel = this.get(); + if (sel) + { + var el = sel.anchorNode; + var block = this.getBlock(el); + var blocks = this.getBlocks(); + + // td, th or hasn't block + if ((block && this.inspector.isTableCellTag(block.tagName)) || (block === false && blocks.length === 0)) + { + return true; + } + } + + return false; + }, + isAll: function(el) + { + var node = this.utils.getNode(el); + if (!node) return false; + + var isEditor = this.editor.isEditor(node); + var data = this.inspector.parse(node); + + // component + if (!data.isFigcaption() && this.component.isNonEditable(node) && this.component.isActive(node)) + { + return true; + } + + if (isEditor) + { + var $editor = this.editor.getElement(); + var output = $editor.html().replace(/

<\/p>$/i, ''); + var htmlLen = this.getHtml(false).length; + var outputLen = output.length; + + if (htmlLen !== outputLen) + { + return false; + } + } + + // editor empty or collapsed + if ((isEditor && this.editor.isEmpty()) || this.isCollapsed()) + { + return false; + } + + // all + var offset = this.offset.get(node, true); + var size = this.offset.size(node, true); + + // pre, table, or pre/code in figure + if (!isEditor && data.isComponentType('code')) + { + size = this.getText().trim().length; + } + + if (offset && offset.start === 0 && offset.end === size) + { + return true; + } + + return false; + }, + + // has + hasNonEditable: function() + { + var selected = this.getHtml(); + var $wrapper = $R.dom('

').html(selected); + + return (!this.isCollapsed() && $wrapper.find('.non-editable').length !== 0); + }, + + // set + setRange: function(range) + { + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + }, + setAll: function(el) + { + var node = this.utils.getNode(el); + if (!node) return; + + var data = this.inspector.parse(node); + + this.component.clearActive(); + + this.editor.focus(); + this.editor.saveScroll(); + this.editor.disableNonEditables(); + + if (node && node.tagName === 'TABLE') + { + var first = data.findFirstNode('td, th'); + var last = data.findLastNode('td, th'); + + $R.dom(first).prepend(this.marker.build('start')); + $R.dom(last).append(this.marker.build('end')); + + this.restoreMarkers(); + } + else if (!data.isFigcaption() && this.component.isNonEditable(node)) + { + this.component.setActive(node); + } + else + { + if (data.isComponentType('code')) + { + node = data.getComponentCodeElement(); + node.focus(); + } + + var range = document.createRange(); + range.selectNodeContents(node); + + this.setRange(range); + } + + this.editor.enableNonEditables(); + this.editor.restoreScroll(); + }, + + // get + get: function() + { + var sel = window.getSelection(); + return (sel.rangeCount > 0) ? sel : null; + }, + getRange: function() + { + var sel = this.get(); + return (sel) ? ((sel.getRangeAt(0)) ? sel.getRangeAt(0) : null) : null; + }, + getTextBeforeCaret: function(num) + { + num = (typeof num === 'undefined') ? 1 : num; + + var el = this.editor.getElement().get(); + var range = this.getRange(); + var text = false; + if (range) + { + range = range.cloneRange(); + range.collapse(true); + range.setStart(el, 0); + text = range.toString().slice(-num); + } + + return text; + }, + getTextAfterCaret: function(num) + { + num = (typeof num === 'undefined') ? 1 : num; + + var el = this.editor.getElement().get(); + var range = this.getRange(); + var text = false; + if (range) + { + var clonedRange = range.cloneRange(); + clonedRange.selectNodeContents(el); + clonedRange.setStart(range.endContainer, range.endOffset); + + text = clonedRange.toString().slice(0, num); + } + + return text; + }, + getPosition: function() + { + var range = this.getRange(); + var pos = { top: 0, left: 0, width: 0, height: 0 }; + if (window.getSelection && range.getBoundingClientRect) + { + range = range.cloneRange(); + var offset = (range.startOffset-1); + range.setStart(range.startContainer, (offset < 0) ? 0 : offset); + var rect = range.getBoundingClientRect(); + pos = { top: rect.top, left: rect.left, width: (rect.right - rect.left) , height: (rect.bottom - rect.top) }; + } + + return pos; + }, + getCurrent: function() + { + var node = false; + var sel = this.get(); + var component = this.component.getActive(); + + if (component) + { + node = component; + } + else if (sel && this.is()) + { + var data = this.inspector.parse(sel.anchorNode); + node = (!data.isEditor()) ? sel.anchorNode : false; + } + + return node; + }, + getParent: function() + { + var node = false; + var current = this.getCurrent(); + if (current) + { + var parent = current.parentNode; + var data = this.inspector.parse(parent); + + node = (!data.isEditor()) ? parent : false; + } + + return node; + }, + getElement: function(el) + { + var node = el || this.getCurrent(); + while (node) + { + var data = this.inspector.parse(node); + if (data.isElement() && data.isInEditor()) + { + return node; + } + + node = node.parentNode; + } + + return false; + }, + getInline: function(el) + { + var node = el || this.getCurrent(); + var inline = false; + while (node) + { + if (this._isInlineNode(node)) + { + inline = node; + } + + node = node.parentNode; + } + + return inline; + }, + getInlineFirst: function(el) + { + var node = el || this.getCurrent(); + while (node) + { + if (this._isInlineNode(node)) + { + return node; + } + + node = node.parentNode; + } + + return false; + }, + getInlineAll: function(el) + { + var node = el || this.getCurrent(); + var inlines = []; + while (node) + { + if (this._isInlineNode(node)) + { + inlines.push(node); + } + + node = node.parentNode; + } + + return inlines; + }, + getBlock: function(el) + { + var node = el || this.getCurrent(); + while (node) + { + var data = this.inspector.parse(node); + var isBlock = this.inspector.isBlockTag(node.tagName); + + if (isBlock && data.isInEditor(node)) + { + return node; + } + + node = node.parentNode; + } + + return false; + }, + getInlinesAllSelected: function(options) + { + if (this.isAll()) return []; + + var inlines = this.getInlines({ all: true }); + var textNodes = this.getNodes({ textnodes: true, inline: false }); + var selected = this.getText().replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + var finalNodes = []; + + if (textNodes.length !== 0) + { + return finalNodes; + } + + if (selected === '') + { + finalNodes = inlines; + } + else if (inlines.length > 1) + { + for (var i = 0; i < inlines.length; i++) + { + if (this._isTextSelected(inlines[i], selected)) + { + finalNodes.push(inlines[i]); + } + } + } + else if (inlines.length === 1) + { + if (this._isTextSelected(inlines[0], selected)) + { + finalNodes = inlines; + } + } + + finalNodes = (options && options.tags) ? this._filterNodesByTags(finalNodes, options.tags) : finalNodes; + + return finalNodes; + }, + getInlines: function(options) + { + var nodes = this.getNodes(); + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var node; + if (options && options.all) + { + node = nodes[i]; + while (node) + { + if (this._isInlineNode(node) && !this._isInNodesArray(filteredNodes, node)) + { + filteredNodes.push(node); + } + + node = node.parentNode; + } + } + else + { + node = this.getInline(nodes[i]); + if (node && !this._isInNodesArray(filteredNodes, node)) + { + filteredNodes.push(node); + } + } + } + + // filter + filteredNodes = (options && options.tags) ? this._filterNodesByTags(filteredNodes, options.tags) : filteredNodes; + filteredNodes = (options && options.inside) ? this._filterInlinesInside(filteredNodes, options) : filteredNodes; + + return filteredNodes; + }, + getBlocks: function(options) + { + var nodes = this.getNodes(); + var block = this.getBlock(); + nodes = (nodes.length === 0 && block) ? [block] : nodes; + + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var node = this.getBlock(nodes[i]); + var $node = $R.dom(node); + if ($node.hasClass('non-editable')) continue; + + if (node && !this._isInNodesArray(filteredNodes, node)) + { + filteredNodes.push(node); + } + } + + // filter + filteredNodes = (options && options.tags) ? this._filterNodesByTags(filteredNodes, options.tags) : filteredNodes; + filteredNodes = (options && options.first) ? this._filterBlocksFirst(filteredNodes, options) : filteredNodes; + + return filteredNodes; + }, + getElements: function(options) + { + var nodes = this.getNodes({ textnodes: false }); + var block = this.getBlock(); + nodes = (nodes.length === 0 && block) ? [block] : nodes; + + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + if (!this._isInNodesArray(filteredNodes, nodes[i])) + { + filteredNodes.push(nodes[i]); + } + } + + // filter + filteredNodes = (options && options.tags) ? this._filterNodesByTags(filteredNodes, options.tags) : filteredNodes; + + return filteredNodes; + }, + getNodes: function(options) + { + var nodes = []; + var activeComponent = this.component.getActive(); + if (activeComponent) + { + nodes = this._getNodesComponent(activeComponent); + } + else if (this.isCollapsed()) + { + var current = this.getCurrent(); + nodes = (current) ? [current] : []; + } + else if (this.is() && !activeComponent) + { + nodes = this._getRangeSelectedNodes(); + } + + // filter + nodes = this._filterServicesNodes(nodes); + nodes = this._filterEditor(nodes); + + // options + nodes = (options && options.tags) ? this._filterNodesByTags(nodes, options.tags) : nodes; + nodes = (options && options.textnodes) ? this._filterNodesTexts(nodes, options) : nodes; + nodes = (options && !options.textnodes) ? this._filterNodesElements(nodes) : nodes; + + return nodes; + }, + + // text & html + getText: function() + { + var sel = this.get(); + return (sel) ? this.utils.removeInvisibleChars(sel.toString()) : ''; + }, + getHtml: function(clean) + { + var html = ''; + var sel = this.get(); + if (sel) + { + var container = document.createElement('div'); + var len = sel.rangeCount; + for (var i = 0; i < len; ++i) + { + container.appendChild(sel.getRangeAt(i).cloneContents()); + } + + html = container.innerHTML; + html = (clean !== false) ? this.cleaner.output(html) : html; + html = html.replace(/

<\/p>$/i, ''); + } + + return html; + }, + + // clear + clear: function() + { + this.component.clearActive(); + this.get().removeAllRanges(); + }, + + // collapse + collapseToStart: function() + { + var sel = this.get(); + if (sel && !sel.isCollapsed) sel.collapseToStart(); + }, + collapseToEnd: function() + { + var sel = this.get(); + if (sel && !sel.isCollapsed) sel.collapseToEnd(); + }, + + // save + saveActiveComponent: function() + { + var activeComponent = this.component.getActive(); + if (activeComponent) + { + this.savedComponent = activeComponent; + return true; + } + + return false; + }, + restoreActiveComponent: function() + { + if (this.savedComponent) + { + this.component.setActive(this.savedComponent); + return true; + } + + return false; + }, + save: function() + { + this._clearSaved(); + + var el = this.getElement(); + var tags = ['TD', 'TH', 'P', 'DIV', 'PRE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'BLOCKQUOTE']; + if (el && (tags.indexOf(el.tagName) !== -1) && (el.innerHTML === '' || el.innerHTML === '
')) + { + this.savedElement = el; + } + else if (!this.saveActiveComponent()) + { + this.saved = this.offset.get(); + } + }, + restore: function() + { + if (!this.saved && !this.savedComponent && !this.savedElement) return; + + this.editor.saveScroll(); + + if (this.savedElement) + { + this.caret.setStart(this.savedElement); + } + else if (!this.restoreActiveComponent()) + { + this.offset.set(this.saved); + } + + this._clearSaved(); + this.editor.restoreScroll(); + }, + saveMarkers: function() + { + this._clearSaved(); + + if (!this.saveActiveComponent()) + { + this.marker.insert(); + } + }, + restoreMarkers: function() + { + this.editor.saveScroll(); + + if (!this.restoreActiveComponent()) + { + this.marker.restore(); + } + + this._clearSaved(); + this.editor.restoreScroll(); + }, + + // private + _getNextNode: function(node) + { + if (node.hasChildNodes()) return node.firstChild; + + while (node && !node.nextSibling) + { + node = node.parentNode; + } + + if (!node) return null; + + return node.nextSibling; + }, + _getNodesComponent: function(component) + { + var current = this.getCurrent(); + var data = this.inspector.parse(current); + + return (data.isFigcaption()) ? [data.getFigcaption()] : [component]; + }, + _getRangeSelectedNodes: function() + { + var nodes = []; + var range = this.getRange(); + var node = range.startContainer; + var startNode = range.startContainer; + var endNode = range.endContainer; + var $editor = this.editor.getElement(); + + // editor + if (startNode === $editor.get() && this.isAll()) + { + nodes = this.utils.getChildNodes($editor); + } + // single node + else if (node == endNode) + { + nodes = [node]; + } + else + { + while (node && node != endNode) + { + nodes.push(node = this._getNextNode(node)); + } + + node = range.startContainer; + while (node && node != range.commonAncestorContainer) + { + nodes.unshift(node); + node = node.parentNode; + } + } + + return nodes; + }, + _isInNodesArray: function(nodes, node) + { + return (nodes.indexOf(node) !== -1); + }, + _filterEditor: function(nodes) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var data = this.inspector.parse(nodes[i]); + if (data.isInEditor()) + { + filteredNodes.push(nodes[i]); + } + } + + return filteredNodes; + }, + _filterServicesNodes: function(nodes) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var $el = $R.dom(nodes[i]); + var skip = false; + + if (nodes[i] && nodes[i].nodeType === 3 && this.utils.isEmpty(nodes[i])) skip = true; + if ($el.hasClass('redactor-script-tag') + || $el.hasClass('redactor-component-caret') + || $el.hasClass('redactor-selection-marker') + || $el.hasClass('non-editable')) skip = true; + + if (!skip) + { + filteredNodes.push(nodes[i]); + } + } + + return filteredNodes; + }, + _filterNodesTexts: function(nodes, options) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + if (nodes[i].nodeType === 3 || (options.keepbr && nodes[i].tagName === 'BR')) + { + var inline = this.getInline(nodes[i]); + var isInline = (inline && options && options.inline === false); + if (!isInline) + { + filteredNodes.push(nodes[i]); + } + } + } + + return filteredNodes; + }, + _filterNodesElements: function(nodes) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + if (nodes[i].nodeType !== 3) + { + filteredNodes.push(nodes[i]); + } + } + + return filteredNodes; + }, + _filterNodesByTags: function(nodes, tags, passtexts) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + if (passtexts && nodes[i].nodeType === 3) + { + filteredNodes.push(nodes[i]); + } + else if (nodes[i].nodeType !== 3) + { + var nodeTag = nodes[i].tagName.toLowerCase(); + if (tags.indexOf(nodeTag.toLowerCase()) !== -1) + { + filteredNodes.push(nodes[i]); + } + } + } + + return filteredNodes; + }, + _filterBlocksFirst: function(nodes) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + var $node = $R.dom(nodes[i]); + var parent = $node.parent().get(); + var isFirst = ($node.parent().hasClass('redactor-in')); + var isCellParent = (parent && (parent.tagName === 'TD' || parent.tagName === 'TH')); + if (isFirst || isCellParent) + { + filteredNodes.push(nodes[i]); + } + } + + return filteredNodes; + }, + _filterInlinesInside: function(nodes) + { + var filteredNodes = []; + for (var i = 0; i < nodes.length; i++) + { + if (window.getSelection().containsNode(nodes[i], true)) + { + filteredNodes.push(nodes[i]); + } + } + + return filteredNodes; + }, + _isTextSelected: function(node, selected) + { + var text = this.utils.removeInvisibleChars(node.textContent); + + return ( + selected === text + || text.search(selected) !== -1 + || selected.search(new RegExp('^' + text)) !== -1 + || selected.search(new RegExp(text + '$')) !== -1 + ); + }, + _isInlineNode: function(node) + { + var data = this.inspector.parse(node); + + return (this.inspector.isInlineTag(node.tagName) && data.isInEditor()); + }, + _clearSaved: function() + { + this.saved = false; + this.savedComponent = false; + this.savedElement = false; + } +}); +$R.add('service', 'element', { + init: function(app) + { + this.app = app; + this.rootElement = app.rootElement; + + // local + this.$element = {}; + this.type = 'inline'; + }, + start: function() + { + this._build(); + this._buildType(); + }, + + // public + isType: function(type) + { + return (type === this.type); + }, + getType: function() + { + return this.type; + }, + getElement: function() + { + return this.$element; + }, + + // private + _build: function() + { + this.$element = $R.dom(this.rootElement); + }, + _buildType: function() + { + var tag = this.$element.get().tagName; + + this.type = (tag === 'TEXTAREA') ? 'textarea' : this.type; + this.type = (tag === 'DIV') ? 'div' : this.type; + this.type = (this.opts.inline) ? 'inline' : this.type; + } +}); +$R.add('service', 'editor', { + init: function(app) + { + this.app = app; + + // local + this.scrolltop = false; + this.pasting = false; + }, + + // start + start: function() + { + this._build(); + }, + + // focus + focus: function() + { + if (!this.isFocus() && !this._isContenteditableFocus()) + { + this.saveScroll(); + this.$editor.focus(); + this.restoreScroll(); + } + }, + startFocus: function() + { + this.caret.setStart(this.getFirstNode()); + }, + endFocus: function() + { + this.caret.setEnd(this.getLastNode()); + }, + + // pasting + isPasting: function() + { + return this.pasting; + }, + enablePasting: function() + { + this.pasting = true; + }, + disablePasting: function() + { + this.pasting = false; + }, + + // scroll + saveScroll: function() + { + this.scrolltop = this._getScrollTarget().scrollTop(); + }, + restoreScroll: function() + { + if (this.scrolltop !== false) + { + this._getScrollTarget().scrollTop(this.scrolltop); + this.scrolltop = false; + } + }, + + // non editables + disableNonEditables: function() + { + this.$noneditables = this.$editor.find('[contenteditable=false]'); + this.$noneditables.attr('contenteditable', true); + }, + enableNonEditables: function() + { + if (this.$noneditables) + { + setTimeout(function() { this.$noneditables.attr('contenteditable', false); }.bind(this), 1); + } + }, + + // nodes + getFirstNode: function() + { + return this.$editor.contents()[0]; + }, + getLastNode: function() + { + var nodes = this.$editor.contents(); + + return nodes[nodes.length-1]; + }, + + // utils + isSourceMode: function() + { + var $source = this.source.getElement(); + + return $source.hasClass('redactor-source-open'); + }, + isEditor: function(el) + { + var node = $R.dom(el).get(); + + return (node === this.$editor.get()); + }, + isEmpty: function(keeplists) + { + return this.utils.isEmptyHtml(this.$editor.html(), false, keeplists); + }, + isFocus: function() + { + var $active = $R.dom(document.activeElement); + var isComponentSelected = (this.$editor.find('.redactor-component-active').length !== 0); + + return (isComponentSelected || $active.closest('.redactor-in-' + this.uuid).length !== 0); + }, + setEmpty: function() + { + this.$editor.html(this.opts.emptyHtml); + }, + + // element + getElement: function() + { + return this.$editor; + }, + + // private + _build: function() + { + var $element = this.element.getElement(); + var editableElement = (this.element.isType('textarea')) ? '

' : $element.get(); + + this.$editor = $R.dom(editableElement); + }, + _getScrollTarget: function() + { + var $target = this.$doc; + if (this.opts.toolbarFixedTarget !== document) + { + $target = $R.dom(this.opts.toolbarFixedTarget); + } + else + { + $target = (this.opts.scrollTarget) ? $R.dom(this.opts.scrollTarget) : $target; + } + + return $target; + }, + _isContenteditableFocus: function() + { + var block = this.selection.getBlock(); + var $blockParent = (block) ? $R.dom(block).closest('[contenteditable=true]').not('.redactor-in') : []; + + return ($blockParent.length !== 0); + } +}); +$R.add('service', 'container', { + init: function(app) + { + this.app = app; + }, + // public + start: function() + { + this._build(); + }, + getElement: function() + { + return this.$container; + }, + + // private + _build: function() + { + var tag = (this.element.isType('inline')) ? '' : '
'; + this.$container = $R.dom(tag); + } +}); +$R.add('service', 'source', { + init: function(app) + { + this.app = app; + + // local + this.$source = {}; + this.content = ''; + }, + // public + start: function() + { + this._build(); + this._buildName(); + this._buildStartedContent(); + }, + getElement: function() + { + return this.$source; + }, + getCode: function() + { + return this.$source.val(); + }, + getName: function() + { + return this.$source.attr('name'); + }, + getStartedContent: function() + { + return this.content; + }, + setCode: function(html) + { + return this.insertion.set(html, true, false); + }, + isNameGenerated: function() + { + return (this.name); + }, + rebuildStartedContent: function() + { + this._buildStartedContent(); + }, + + // private + _build: function() + { + var $element = this.element.getElement(); + var isTextarea = this.element.isType('textarea'); + var sourceElement = (isTextarea) ? $element.get() : '' - + ''; - }, - init: function() - { - var button = this.button.addAfter('image', 'video', this.lang.get('video')); - this.button.addCallback(button, this.video.show); - }, - show: function() - { - this.modal.addTemplate('video', this.video.getTemplate()); + $R.add('plugin', 'video', { + translations: { + en: { + "video": "Video", + "video-html-code": "Video Embed Code or Youtube/Vimeo Link" + } + }, + modals: { + 'video': + '
\ +
\ + \ + \ +
\ +
' + }, + init: function(app) + { + this.app = app; + this.lang = app.lang; + this.opts = app.opts; + this.toolbar = app.toolbar; + this.component = app.component; + this.insertion = app.insertion; + this.inspector = app.inspector; + }, + // messages + onmodal: { + video: { + opened: function($modal, $form) + { + $form.getField('video').focus(); + }, + insert: function($modal, $form) + { + var data = $form.getData(); + this._insert(data); + } + } + }, + oncontextbar: function(e, contextbar) + { + var data = this.inspector.parse(e.target) + if (data.isComponentType('video')) + { + var node = data.getComponent(); + var buttons = { + "remove": { + title: this.lang.get('delete'), + api: 'plugin.video.remove', + args: node + } + }; - this.modal.load('video', this.lang.get('video'), 700); - this.modal.createCancelButton(); + contextbar.set(e, node, buttons, 'bottom'); + } - var button = this.modal.createActionButton(this.lang.get('insert')); - button.on('click', this.video.insert); + }, - this.selection.save(); - this.modal.show(); + // public + start: function() + { + var obj = { + title: this.lang.get('video'), + api: 'plugin.video.open' + }; - $('#redactor-insert-video-area').focus(); + var $button = this.toolbar.addButtonAfter('image', 'video', obj); + $button.setIcon(''); + }, + open: function() + { + var options = { + title: this.lang.get('video'), + width: '600px', + name: 'video', + handle: 'insert', + commands: { + insert: { title: this.lang.get('insert') }, + cancel: { title: this.lang.get('cancel') } + } + }; - }, - insert: function() - { - var data = $('#redactor-insert-video-area').val(); + this.app.api('module.modal.build', options); + }, + remove: function(node) + { + this.component.remove(node); + }, - if (!data.match(/]*>/gi; + + data = data.replace(/]?)>([\w\W]*?)<\/p>/gi, ''); + data = data.replace(tags, function ($0, $1) + { + return (allowed.indexOf($1.toLowerCase()) === -1) ? '' : $0; + }); + } + else + { + if (data.match(this.opts.regex.youtube)) + { + data = data.replace(this.opts.regex.youtube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd); + } + else if (data.match(this.opts.regex.vimeo)) + { - this.code.sync(); + data = data.replace(this.opts.regex.vimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd); + } } - }; - }; -})(jQuery); \ No newline at end of file + + return data; + } + }); +})(Redactor); \ No newline at end of file diff --git a/app/assets/javascripts/comfy/vendor/redactor/widget.js b/app/assets/javascripts/comfy/vendor/redactor/widget.js new file mode 100644 index 000000000..8ce7dfd83 --- /dev/null +++ b/app/assets/javascripts/comfy/vendor/redactor/widget.js @@ -0,0 +1,153 @@ +(function($R) +{ + $R.add('plugin', 'widget', { + translations: { + en: { + "widget": "Widget", + "widget-html-code": "Widget HTML Code" + } + }, + modals: { + 'widget': + '
\ +
\ + \ + \ +
\ +
' + }, + init: function(app) + { + this.app = app; + this.lang = app.lang; + this.opts = app.opts; + this.toolbar = app.toolbar; + this.component = app.component; + this.insertion = app.insertion; + this.inspector = app.inspector; + this.selection = app.selection; + }, + // messages + onmodal: { + widget: { + opened: function($modal, $form) + { + $form.getField('widget').focus(); + + if (this.$currentItem) + { + var code = decodeURI(this.$currentItem.attr('data-widget-code')); + $form.getField('widget').val(code); + } + }, + insert: function($modal, $form) + { + var data = $form.getData(); + this._insert(data); + } + } + }, + oncontextbar: function(e, contextbar) + { + var data = this.inspector.parse(e.target) + if (!data.isFigcaption() && data.isComponentType('widget')) + { + var node = data.getComponent(); + var buttons = { + "edit": { + title: this.lang.get('edit'), + api: 'plugin.widget.open', + args: node + }, + "remove": { + title: this.lang.get('delete'), + api: 'plugin.widget.remove', + args: node + } + }; + + contextbar.set(e, node, buttons, 'bottom'); + } + }, + onbutton: { + widget: { + observe: function(button) + { + this._observeButton(button); + } + } + }, + + // public + start: function() + { + var obj = { + title: this.lang.get('widget'), + api: 'plugin.widget.open', + observe: 'widget' + }; + + var $button = this.toolbar.addButton('widget', obj); + $button.setIcon(''); + }, + open: function() + { + this.$currentItem = this._getCurrent(); + + var options = { + title: this.lang.get('widget'), + width: '600px', + name: 'widget', + handle: 'insert', + commands: { + insert: { title: (this.$currentItem) ? this.lang.get('save') : this.lang.get('insert') }, + cancel: { title: this.lang.get('cancel') } + } + }; + + this.app.api('module.modal.build', options); + }, + remove: function(node) + { + this.component.remove(node); + }, + + // private + _getCurrent: function() + { + var current = this.selection.getCurrent(); + var data = this.inspector.parse(current); + if (data.isComponentType('widget')) + { + return this.component.build(data.getComponent()); + } + }, + _insert: function(data) + { + this.app.api('module.modal.close'); + + if (data.widget.trim() === '') + { + return; + } + + var html = (this._isHtmlString(data.widget)) ? data.widget : document.createTextNode(data.widget); + var $component = this.component.create('widget', html); + $component.attr('data-widget-code', encodeURI(data.widget.trim())); + this.insertion.insertHtml($component); + + }, + _isHtmlString: function(html) + { + return !(typeof html === 'string' && !/^\s*<(\w+|!)[^>]*>/.test(html)); + }, + _observeButton: function(button) + { + var current = this.selection.getCurrent(); + var data = this.inspector.parse(current); + + if (data.isComponentType('table')) button.disable(); + else button.enable(); + } + }); +})(Redactor); \ No newline at end of file diff --git a/app/assets/stylesheets/comfy/admin/cms/application.sass b/app/assets/stylesheets/comfy/admin/cms/application.sass index bc469631d..181cdb3b6 100644 --- a/app/assets/stylesheets/comfy/admin/cms/application.sass +++ b/app/assets/stylesheets/comfy/admin/cms/application.sass @@ -3,6 +3,11 @@ @import "comfy/vendor/codemirror" @import "comfy/vendor/fontawesome" @import "comfy/vendor/redactor" +@import "comfy/vendor/redactor/clips" +@import "comfy/vendor/redactor/filemanager" +@import "comfy/vendor/redactor/handle" +@import "comfy/vendor/redactor/inlinestyle" +@import "comfy/vendor/redactor/variable" @import "comfy/vendor/flatpickr.min" @import "comfy/admin/cms/codemirror_overrides" diff --git a/app/assets/stylesheets/comfy/vendor/redactor.css b/app/assets/stylesheets/comfy/vendor/redactor.css old mode 100755 new mode 100644 index 03fd6bf0d..4b01a8d53 --- a/app/assets/stylesheets/comfy/vendor/redactor.css +++ b/app/assets/stylesheets/comfy/vendor/redactor.css @@ -1,948 +1,1355 @@ /* - Icon font + Redactor 3 + + http://imperavi.com/redactor/ + + Copyright (c) 2009-2018, Imperavi LLC. + License: http://imperavi.com/redactor/license/ */ +@keyframes fadeIn { + from { + opacity: 0; } + to { + opacity: 1; } } + +@keyframes fadeOut { + from { + opacity: 1; } + to { + opacity: 0; } } + +.redactor-animate-hide { + display: none !important; } + +.redactor-fadeIn { + opacity: 0; + animation: fadeIn .2s ease-in-out; } + +.redactor-fadeOut { + opacity: 1; + animation: fadeOut .2s ease-in-out; } + @font-face { - font-family: 'RedactorFont'; - src: url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype'), url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AABIoAAoAAAAAEeAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAADgEAAA4Bg0Rie09TLzIAAA74AAAAYAAAAGAIIvzVY21hcAAAD1gAAABMAAAATBpVzHZnYXNwAAAPpAAAAAgAAAAIAAAAEGhlYWQAAA+sAAAANgAAADYACVb9aGhlYQAAD+QAAAAkAAAAJAPhAgVobXR4AAAQCAAAAJAAAACQQQED3m1heHAAABCYAAAABgAAAAYAJFAAbmFtZQAAEKAAAAFmAAABZhHEcG1wb3N0AAASCAAAACAAAAAgAAMAAAEABAQAAQEBDVJlZGFjdG9yRm9udAABAgABADr4HAL4GwP4GAQeCgAZU/+Lix4KABlT/4uLDAeKZviU+HQFHQAAAT8PHQAAAUQRHQAAAAkdAAAN+BIAJQEBDRkbHSAlKi80OT5DSE1SV1xhZmtwdXp/hImOk5idoqessba7wFJlZGFjdG9yRm9udFJlZGFjdG9yRm9udHUwdTF1MjB1RTYwMHVFNjAxdUU2MDJ1RTYwM3VFNjA0dUU2MDV1RTYwNnVFNjA3dUU2MDh1RTYwOXVFNjBBdUU2MEJ1RTYwQ3VFNjBEdUU2MEV1RTYwRnVFNjEwdUU2MTF1RTYxMnVFNjEzdUU2MTR1RTYxNXVFNjE2dUU2MTd1RTYxOHVFNjE5dUU2MUF1RTYxQnVFNjFDdUU2MUR1RTYxRXVFNjFGAAACAYkAIgAkAgABAAQABwAKAA0AQQCYAPEBSQH6Ai8CxwMhA98EGwTXBYEFkQW0BfEGLwagBxEHOgf0CLUJaQmsCfwKhAq5C0QLdAuiC9AMAQxo/JQO/JQO/JQO+5QOi7AVi/gB+JSLi/wB/JSLBfhv990V/EqLi/u5+EqLi/e5Bfu4+5QVi/dv9yb7Avsm+wEFDvcm+AIV+AKLi0L8AouL1AWL+wIV+AKLi0L8AouL1AWL+wIV+AKLi0L8AouL1AX7JvdwFdSLi0JCi4vUBYv7AhXUi4tCQouL1AWL+wIV1IuLQkKLi9QFDviLsBVky0yq+0KWCIshBYuLQMb7LPcT9z33GsW4i4sIiyEF92Wr9wT7QV77Cgj7yfdpFYvIBYuLb3ImSOFBtnqLiwiLfIvXBe6F9yJ7nGSl0PsO6Ps2YwgO9wLUFfe4i4tn+7iLi68FysoVnHmngrGLsounlJydnJ2Up4uyCIv3SUyLi/tXBYt8hoCDg4ODgId8i32Lf4+Dk4OTh5aLmgiL91dLi4v7SQWLZJRvnXkIDvfd+EoVrouL+yrWi4tr+wKLi/dKBbH7kxX3JS/7JS+L1fsDi4uw9wOLi9QF+3LTFfsl5/cl54tC9wOLi2b7A4uLQQWXNhWTg499i3iLf4mBhoSGg4SHgYmOio6KjYiNiI6GjoQIpklri3i5BYuMio2KjYaZhZKEiwiBi4tDbouL90q1iwWfi5mHk4MIVEcVmYsFk4uRjY+Pjo+NkYuUi5SJkoiOh4+FjYOLCH2Li1kFDve393oVRYuu9wyu+wwF+0r7DRXVi6LU7ouiQtWLJve6MIsm+7oFjGcV97iLi0L7uIuL1AUOi7AVi/gB+JSLi/wB/JSLBfdLrxX3JouL1Psmi4tCBYv3AhX3JouL1Psmi4tCBWb3SxX7AYuLQvcBi4vUBYv7AhX7AYuLQvcBi4vUBYv7AhX7AYuLQvcBi4vUBbD3cBWLQvcmi4vU+yaLBfe4ixX7AYuLQvcBi4vUBYv7AhX7AYuLQvcBi4vUBYv7AhX7AYuLQvcBi4vUBQ74lPdzFfss+xNAUIuLCIv1BftCgExsZEte9wr3BPdB92VrCIv1BYuLxV73PfsaCPxYLBWcsvcim+6RCIs/i5oFi4u2nOHVJs5vpIuLCItOBfs2s/sOLqVGCA73zfe2FXNsgGiLY4tpk3Ccd513n4Gji6CLnJKZmpqakpyLn4uehZt+mH+ZfJJ7i32LgIeChQiIiYmKiYuKi4mMioyKjoqPi5GLpJOknKOco6KcqJYIi6EFWXhlcnRrCPthixV0bH9oi2OLaZNwnXecd6CBoougi5ySmpqZmpKci5+LnoWbfph/mX2Seot+i3+IgoQIiImJioqLiYuKjIqMiY6Kj4uRi6SUpJujnKOinKmWCIuhBVh4ZnJzawgOi/gCFfiUi4tC/JSLi9QF90v7AhX33YuLQvvdi4vUBYv7AhX33YuLQvvdi4vUBWZCFYv3S/snL/cnMAUO9yb4AhX4AouLQvwCi4vUBYv7AhX4AouLQvwCi4vUBYv7AhX4AouLQvwCi4vUBfsh9hXPi4ufc4uL6HeLdYWLd6GRi0Jzi4t3Bav7JRWXl5KTjY6PkI2PjY+Mj4yPi5CLlIiThJCFkYKOf4uHi4aKhoqGioaKhokIi3YFkI6QjZCNkIyPjI+LkIuPio6IjoiMh4uGi4iLiImIiYeJh4eHiIiDgX18CIB+i3jPi4ufXosFjo+QkJGRCIuLBQ74AtQVcItyk3aYCIu/qYsFmIWZh5uLvYu0sIu5i7pisFmLe4t9h36FCG2Li78FoJikk6aL3IvMSYs6iztKSTqLCPtL90sV9yaLi0L7JouL1AVmuhV8i3yHfoUIbYuLcwWAfYR6i3iLeZJ5ln0Ii3SpiwWYhZqHmoubi5mPmJEIqYuLVwV2fnKDcIs6i0rNi9uL3MzN3Iumi6SDoH4Ii1dtiwV+kX2Pe4sIDov3lBX4lIuLQvyUi4vUBQ73m/ftFWL7a0qLgFL3VYuWxEuLtPdry4uWxPtVi4BSzIsFDov4AhX4lIuLQvyUi4vUBfdL+wIV992Li0L73YuL1AWL+wIV992Li0L73YuL1AX7S0IVi/dL9ycv+ycwBQ6LsBWL+AH4lIuL/AH8lIsF+G/33RX8SouL+7n4SouL97kF+0r7SxWvi7vqySyLQvwCi4vU9wL3JvcC+yYFDvhv+EsVi/tw+2/3cPdviwVhYBWShIyChoUI+wf7BwWFhoKMhJKEkoqUkJEI9wj3BwWQkJWKkYQI/CD8HxX3b4r7b/dvi/tuBbW1FZKElYqQkAj3B/cHBZCQipWEkoSRgo2FhQj7BvsHBYWGjYGRhQgO97n3kxWL93D3b/tv+2+KBbW3FYSSipSQkQj3B/cGBZGRlIqShJKEjIGGhgj7CPsHBYaGgYyFkgj7CPsJFftvjPdv+3CL928FYWEVhJKBjIaGCPsH+wcFhoaMgZKEkoSUipGRCPcG9wYFkZGJlIWSCA733bAVi/fdZ4uL+91Bi4v3JgVPi1q8i8iLx7y8x4sI9yeLi/wBZosFDvgm9yYV1Ysv+yUv9yXVi4v3J0GL5/cl5/slQYuL+ycF+3+EFYWCgoSBhoGGgIh/i3WLeZF+mH6XhZ2Looujkp2blpqXopGriwiwi4uUBYuUiJKFj4SQgo1/i3+Lf4l/iH+If4V+hAiLugWWkJeOl46XjZiMmIusi6KEmH6ZfZFyi2gIi/sMV4uLowWL1hV2iwV3i32IhIaDhoeCi36LgY6EkIWQhpOIlIuZi5aQkpaTlo+ai58Ii48FDvdC91kVVoum9wml+wkF+x37ChXDi5zS1oudRMOLPvezR4s++7MF+BPwFYuHBYt3h3uDgIOAf4V9i4GLg46GkYWRiJOLlIuYj5WTkJSQmY6giwihiwWt7RV9mXOSaYt8i36Kfol/iH6Hf4YIi1sFmJOYkJiPl46YjZmLl4uViJGHkoaOhIuCCIuCZYsFaYtyhXt/e3+DeItyi3SReZl+mH6ehaOLmIuXjZWQlpCTk5KUCItzwouL9w8Fi6+EpX2ZCA7U95QV+AKLi2b8AouLsAX3U1oVloeUhZGEkYSOgouCi36GgYKEgoR/iHuLe4t6jnuRepB6lHqXCItKBZqEm4Wch5yIm4mci7OLqZOfm5+alKOLq4ujhZ9/mn6bd5dwlAhvlgV3kX6ShZGFkIiTi5OLl4+UlJGTkZeOm4uai5mImoaZhpqEmYIIi8gFfJF8kHuPfI58jXuLaYtxg3h6d3uCdItui3WQeZd+l32hf61+CKuABQ6L928Vr6n3S/snZ277S/cmBYuLFfdL9yevbvtL+ydnqAX4lIsVZ6n7S/snr273S/cmBYuLFftL9ydnbvdL+yevqAUOi2YVi/iU+JSLi/yU/JSLBfhv+HAV/EqLi/xL+EqLi/hLBUL7JhX7uIuL1Pe4i4tCBYv7AhX7uIuL1Pe4i4tCBYv7AhX7uIuL1Pe4i4tCBQ73jPdyFZ6LmYiUg5ODj36LeYt6h3+DhIOEfYd3iwhii4vstIsFi/cVFZuLloiShJKFjoKLfYt+iIGEhYSFgIh7iwhii4vYtIsFJvuqFfCLBbWLqJKemp2ZlKKLqoulhZ9/mn+ZeZRzjZ+NmpKVl5aXkJuLoIungqB5mHqZcJJoiwgmi4v73QUOsIsVi/hL+EqLi/xL/EqLBfeR+AIVR4s/+7nDi5vT1oucQ8KLQPe5BWlWFaX7DFeLpfcMBQ74UPeKFfso+yiHjwV9h3uNfJMIamupbXx8BWJiSYtitAh8mgVitIvNtLQI92v3awW0tM2LtGIImnwFtGKLSWJiCGb3EhVuqFyKbm4I+1n7WgVtbotcp26ob7qLqKkIsrEFg4+EkIWScKaGsJ+gCN3dBZuapIyifwj7EvsRsWb3GvcaBaiojLpuqAgOi/gCFfiUi4tC/JSLi9QF9yb7AhX4AouLQvwCi4vUBfcn+wIV92+Li0L7b4uL1AUOi/gCFfiUi4tC/JSLi9QFi/sCFfgBi4tC/AGLi9QFi/sCFfdwi4tC+3CLi9QFDov4AhX4k4uLQvyTi4vUBYv7AhX4k4uLQvyTi4vUBYv7AhX4lIuLQvyUi4vUBQ73AvgCFfe4i4tC+7iLi9QF+wL7AhX4lIuLQvyUi4vUBfcC+wIV97iLi0L7uIuL1AUO1LIVi9RCi4v3ufhLi4tB1IuL+7j8S4sF99333RX8AYuL+3D4AYuL93AF1UIVZouL+0v73YuLZvgCi4v3cAX7b0IV+0yL5/cB5/sBBfcBZhX7uYuLsPe5i4tmBWL3AhW0QkKLq9QFDviUFPiUFYsMCgAAAAADAgABkAAFAAABTAFmAAAARwFMAWYAAAD1ABkAhAAAAAAAAAAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAEAAAOYfAeD/4P/gAeAAIAAAAAEAAAAAAAAAAAAAACAAAAAAAAIAAAADAAAAFAADAAEAAAAUAAQAOAAAAAoACAACAAIAAQAg5h///f//AAAAAAAg5gD//f//AAH/4xoEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAQAAhlBJsl8PPPUACwIAAAAAAM91iyUAAAAAz3WLJf///9sCAAHbAAAACAACAAAAAAAAAAEAAAHg/+AAAAIA//8AAAIAAAEAAAAAAAAAAAAAAAAAAAAkAAAAAAAAAAAAAAAAAQAAAAIAAAACAAAAAgAAAAIAAG4CAAAAAgAAbQIAAAACAAAJAgAASQIA//8CAAAAAgAAAAIAAAACAACSAgAAAAIAAAACAAAlAgAAAAIAAG4CAAAlAgAAJQIAAEkCAAAAAgAAAAIAAJMCAAAlAgAAQgIAAAACAAAAAgAAAAIAAAACAAAAAABQAAAkAAAAAAAOAK4AAQAAAAAAAQAYAAAAAQAAAAAAAgAOAGoAAQAAAAAAAwAYAC4AAQAAAAAABAAYAHgAAQAAAAAABQAWABgAAQAAAAAABgAMAEYAAQAAAAAACgAoAJAAAwABBAkAAQAYAAAAAwABBAkAAgAOAGoAAwABBAkAAwAYAC4AAwABBAkABAAYAHgAAwABBAkABQAWABgAAwABBAkABgAYAFIAAwABBAkACgAoAJAAUgBlAGQAYQBjAHQAbwByAEYAbwBuAHQAVgBlAHIAcwBpAG8AbgAgADEALgAwAFIAZQBkAGEAYwB0AG8AcgBGAG8AbgB0UmVkYWN0b3JGb250AFIAZQBkAGEAYwB0AG8AcgBGAG8AbgB0AFIAZQBnAHUAbABhAHIAUgBlAGQAYQBjAHQAbwByAEYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format('woff'); + font-family: 'Redactor'; + src: url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBkcAAAC8AAAAYGNtYXAXVtKwAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zuxdz/8AAAF4AAAkQGhlYWQPMte3AAAluAAAADZoaGVhB7gD6wAAJfAAAAAkaG10eK4BD60AACYUAAAAuGxvY2GyjqiGAAAmzAAAAF5tYXhwADkBcwAAJywAAAAgbmFtZVDOJQoAACdMAAABknBvc3QAAwAAAAAo4AAAACAAAwP0AZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpKQPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6Sn//f//AAAAAAAg6QD//f//AAH/4xcEAAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAADAAAAgAQAAwAAEgAoADwAABMhMhYVMRUUBiMhIiY1MTU0NjMDITIWFTEVFAYjITgBMSImPQE0NjMxEyEyFhUxFRQGIzEhIiY1MTU0NjO7AooZIiIZ/XYZIiIZgAOKGSIiGfx2GCMiGYACihkiIhn9dhkiIhkDACIZChkiIhkKGSL/ACIZCRkiIhgKGSL/ACIZChkiIhkKGSIAAAADAAAAgAQAAwAAEwApAD4AABMhMhYVMRUUBiMhIiY1MTU0NjMxESEyFhUxFRQGIyE4ATEiJj0BNDYzMREhMhYVMRUUBiMxISImNTE1NDYzMTsCyhkiIhn9NhkiIhkDihkiIhn8dhgjIhkCyhkiIhn9NhkiIhkDACIZChkiIhkKGSL/ACIZCRkiIhgKGSL/ACIZChkiIhkKGSIAAAADAAAAgAQAAwAAEgAoADwAABMhMhYVMRUUBiMhIiY1MTU0NjMDITIWFTEVFAYjITgBMSImPQE0NjMxEyEyFhUxFRQGIzEhIiY1MTU0NjP7AsoZIiIZ/TYZIiIZwAOKGSIiGfx2GCMiGcACyhkiIhn9NhkiIhkDACIZChkiIhkKGSL/ACIZCRkiIhgKGSL/ACIZChkiIhkKGSIAAAADARkARwLnAwAAKwA0AFUAACU+ATU0JjUxNjQ1NCYvAT4BNzE8ATU0JicxLgEjKgEjMSIHETMyFjMyNjcHAzIVFCMiIzUzAzoBMzIWFyceARUcARU1HAEVFAYHFQ4BIyImIzMiJzUzAp8hJwEBSDcCJjABIRshUS0CBQI4iM8DBgMwWCQByWp3HxxGBwUJBRw0FgEQEhUSFjQcBAkEARcmQIAZSiwBBAEDBwM9XRABEEUrAQMBJj8WGBsG/U4BHxsBAhdPWqb+/w4OAQ4oFwIFAgECBQIZLA4BDQ8BA+MAAAIAwAAAA0ADgAAsADgAACUiJicxJwcOASM4ATkBKgEjIiYnNRE+ATM6ATMxIToBMzIWFzERDgEjKgEjMQERNz4BMzIWFzEXEQMACxMI2toIEwsBAgEYIgICIhgBAgECAAECARgiAgIiGAECAf5AmgcUCgsTCJsABwWOjgYGIBcBAxAYICEX/PAXIQMQ/ZhlBgYGBmUCaAAABQBAAAADwAOAABMAFwAnADcARwAAASEiBhUxERQWMzEhMjY1MRE0JiMRIREhBSEyFhUUBiMxISImNTQ2MxUhMhYVFAYjMSEiJjU0NjMVITIWFRQGIzEhIiY1NDYzA0D9gDVLSzUCgDVLSzX9gAKA/gABgBslJRv+gBslJRsBgBslJRv+gBslJRsBgBslJRv+gBslJRsDgEs1/YA1S0s1AoA1S/0AAoBAJRsbJSUbGyXAJRsbJSUbGyXAJRsbJSUbGyUAAAMAQAAAA8ADgAATABcAWQAAASEiBhUxERQWMzEhMjY1MRE0JiMRIREhAzEHDgEjOAE5ASoBIyImJzE0NjcxNyMwIjEiJicxNDY3MTc+ATM4ATkBOgEzMhYXMRQGBzEHMzAyMTIWFzEUBgcxA0D9gDVLSzUCgDVLSzX9gAKAiMAFEAkBAQEOFAIEBJTpAg4VAQQEwAUQCQEBAQ4UAgQElOkCDhUBBAQDgEs1/YA1S0s1AoA1S/0AAoD+reAGBxIOBQoErRIOBQoE4AYHEg4FCgStEg4FCgQAAAEAgAA8A4ADAQCDAAABIy4BJyMnLgEnFy4BJzUuATUwNDUxPAE1NDY3MT4BMzoBMzEeARcjHgEXIzcuASMqAQczKgEjIgYHMQ4BFRwBFTEcARUUFhc1HgEfAiEVIRceARUxHAEVFAYHMQ4BIyoBIzEuAScXLgEnFwceATM6ATMjMhYzMjY3MT4BNzEuAScXMwOA9BIoFwI5FSMRAQgOBQUFDw0NIxMCBAITJBECEB0NARscQiQHDgYBAgQCJkQaGR0PDRIxHQIv/p0BxAQJChIPESoYAQQBEyQRAhIhDgEiH0wpAgQDAQMHAyxOIB0iAQEIBwHTAYASHQsbChUNAQcSCgEKFw0BAQEBARQiDQsOAQYGBA4IURETARsYFz8lAQMBAQICHDUXARkmCwEWQAQOIREBAQEWJg4OEAEHBgEGEAsBVhUWAR0aF0QoFCYSAgAAAAACAEAAAAPAA4AAHQA9AAABBzMyFhUUBisBIiY1MTU0NjMyFhUxFTceARcVFDY/ASMiJjU0NjMxMzIWFTEVFAYjIiY1MTUHLgEnNTQGNwHA50cTGhoTsxMaGhMSG+YYIgYGeudHExoaE7MTGhoTEhvmGCIGBgYBQOYbEhMaGhOzExoaE0fnBSMXAQkP+uYbEhMaGhOzExoaE0fnBSMXAQkPBgAAAAMAQABAA8ADgAAbAB4AIgAAASMVMxEhETM1IyIGFTERFBYzMSEyNjUxETQmIwEHISczESMDQICA/YCAgDVLSzUCgDVLSzX+wIABAKpUVAKAgP7AAUCASzX+wDVLSzUBQDVLAQDaIP66AAAAAAMAQAAAA8ADgAADABcAHwAAATMnBwEhIgYVMREUFjMxITI2NTERNCYjAycjByMTMxMBuI1GRwGI/YA1S0s1AoA1S0s1sivIKHPoLuoBgdHRAf9LNf2ANUtLNQKANUv9QHNzAkD9wAAABAA/AMADvwL/AAcACgB5ALAAACUDIwMzNzMXJyM3AREwNDU0JicXLgEnMS4BJyMuASMqATkBOAExIgYHMwYHBgcGBxU+ATczPgEzMToBMzIWFzEeARUwFBUxFSMmIiMiBgczDgEVHAEVNRQwFRQWFyceARcxHgEXMx4BMzoBMzE4ATEyNjcjPgE3MRczJw4BDwEOAQcjMCIjIiYnMS4BJzEuASc1LgE1OAE5ATQwNTQ2NzE+ATcxPgE3Mz4BMzoBMzEzFQJA6i7pcynIK0mMRgKABgUBBQ8JChcNAQ0eDwECChMKAhMREBAPDA0fEQMOIBACBAIQHgwKC04DBwQfOBgBFBcEBAEECwcIEwoBCxoOAQIBEiIQAhMfDQE7Qg0eEQELGg4BAQEHDgYGCQQEBQICAQMCAwgGBg4IAQgUCwEBAVPAAj/9wXNzwNH+egEEAgEOGgwBDBMIBwwDBAQBAQIEAwQEBTsGCgQEBAsJChoPAgEhARIPDiwaAQICAQEBCxUKAQoRBwcLBAQFCAYIFg4zaAwWCAEGBwEDAgIGBAQIBQEECwUBAQcOBgYLBAUHAgMCQwAAAAAHAD8AQAPRA0AABwAKAA4AEgAWABoAHgAAJQMjAzM3MxcnIzclMxEjExcHJzcHFzcDNycHFyc3FwJA6i7pcynIK0mMRgHBQEAkLbUtrS21La0ttS2tLbUtwAI//cFzc8DR7/0AAvEttS21LbUt/dMttS21LbUtAAABAMAAQANAA0AAMgAAASEqASMiBgczDgEHMw4BBxUOARUcARUxFBYXNR4BFzEeARczHgEzOgEzMREzETMRMxEzA0D+YAIFAhgsFQIUJA8BDxYIBgcJBwgXDg4hEwEULRgCBAFcgFxoA0AHBgYUDAwdEQEPIhICBQITJBABER4MDRUHCAf+cwKr/VUCqwAAAAAC//EAswQEAtwABQALAAABLQEnCQEBDQEXCQEBn/8AAQBR/qMBXQEJAQD/AFABXf6jAQDIxk7+7P7rAdzIxk0BFAEUAAYAQABAA8ADQAADAB8AIgAlADgATAAAAREhEQU6ATMyFhcxDgEjKgEjMSoBIyImJzE+ATM6ATMHNxcxNxcBITIWFTEVFAYjISImNTE1NDYzESEyFhUxFRQGIzEhIiY1MTU0NjMBAAIA/qsBAgIVHgMDHhUCAgEBAwEVHwICHxUBAwFcTUh0bf22AwoZIiIZ/PYZIiIZAwoZIiIZ/PYZIiIZAoD+gAGAShwUFRsbFRQc9lBQgIACACIZChkiIhkKGSL9gCIZChkiIhkKGSIAAAAHAEAAgAPAAwAAEgApAD0AQQBeAGEAZAAAATMyFhUxFRQGKwEiJjUxNTQ2MxEzMhYVMRUUBiMxIzgBMSImPQE0NjMxASEyFhUxFRQGIzEhIiY1MTU0NjMDESERBTAyMzIWFzEOASMqATkBKgExIiYnMT4BMzAyMzEHNxcjNxcCu8oZIiIZyhkiIhnJGSIiGckYIyIZ/cADChkiIhn89hkiIhk7AcD+1gEBFB0BAR0UAQEBARQdAQEdFAEBUUNAA2hgAwAiGQoZIiIZChki/wAiGQkZIiIYChki/wAiGQoZIiIZChkiAgD+gAGAShwUFBwcFBQc9lBQgIAAAAAHAEAAgAPAAwAAEwAtAEEARQBaAF0AYAAAEzMyFhUxFRQGIzEjIiY1MTU0NjMRMzIWFTEVFDAxFAYjOAExIyImNTE1NDYzMREhMhYVMRUUBiMxISImNTE1NDYzAREhEQUwMjMyFhcxDgEjIiYnMT4BMzoBMwc3Fyc3F3vKGSIiGcoZIiIZyhkiIhnKGSIiGQMKGSIiGfz2GSIiGQGFAcD+1gEBFB0BAx0VFB0DAR0UAQEBUURABGVjAwAiGQoZIiIZChki/wAiGQkBGSIiGQoZIv8AIhkKGSIiGQoZIgIA/oABgEocFBQbGxQUHPZPUAGAgAAABAAAAAAEAAOAAAMAIAAjACYAABMRIREFOgEzMhYXMQ4BIyoBIzEqASMiJicxPgEzOgEzMQM3FzUbAQAEAP1UAQEBL0MBAUMvAQEBAQEBL0MBAUMvAQEBt5uR6NoDgPyAA4CsQS8vQUEvL0H9xbe7BAEn/tQAAAAABQAAAIAEAAMAABMAKgA+AEEARQAAEyEyFhUxFRQGIyEiJjUxNTQ2MzEBITIWFTEVFAYjMSE4ATEiJj0BNDYzMREhMhYVMRUUBiMxISImNTE1NDYzJwcRBzMVIzsDihkiIhn8dhkiIhkBwAHJGSIiGf43GCMiGQHKGSIiGf42GSIiGbvAgICAAwAiGQoZIiIZChki/wAiGQkZIiIYChki/wAiGQoZIiIZChkiIaEBQIBAAAAAAgDAAAADgAOAACQASQAAJTEqASMiJy4BJyYnNDc+ATc2MzIXHgEXFhUxBgcOAQcGIyoBIzU6ATMyNz4BNzY3MTQnLgEnJiMiBw4BBwYVFhceARcWMzoBMzECIAEDAUc/P14bHAExMHw5OREROTl8MDEBHBtePz9HAQMBAQIBNS8vRxQVASYlXisqCgoqK14lJgEVFEcvLzUBAgEAGxpdPj5HR2BfrTw8PDytX2BHRz4+XRobVRQURi4vNTVKSYUvLy8vhUlKNTUvLkYUFAAAAAEBswBIAowC9gADAAAlEyMDAhN5X3pIAq79UgABAAABgQQAAgAAFQAAEyEyFhUxFRQGIyE4ATEiJj0BNDYzMTsDihkiIhn8dhgjIhkCACIZCRkiIhgKGSIAAAAAAwAAAL8EAALBADgAPAB5AAAlIiYnFzUzHgE7ATIWMzI2PwEuASMqASMzDgEHNyM1PgE7ATI2MzIXHgEXFhcVBgcOAQcGIyImIzMBIRUhJw4BBzcjFQ4BFRQWFzEVMx4BMzI2NwczFQ4BKwEiBiMiJy4BJyYnNTY3PgE3NjMyFjMjMhYXJxUjLgEnIwLcKUshAj0TKxcBAwkEQF4IAQhfQAQJBAEYLBQCPSBKKAEDCQU2MTBKFxcEBBcXSjAxNgUJBAH+kQEl/ttJFywUAjwSExMSPBMrFxgsFAI9IEooAQMJBTYxMEoXFwQEFxdKMDE2BQkEASlLIQI9EysXAcAUEgFbCAoBVD4BP1QBCgkBWxEUARQURS8vNQE2Ly9FFBQBAUCA0wEKCQEpES0ZGi0RKAgKCgkBWxEUARQURS8vNQE2Ly9FFBQBFBIBWwkJAQAABQAaAIAEAAMAABIAKAA8AEYAdAAAASEyFhUxFRQGIyEiJjUxNTQ2MxEhMhYVMRUUBiMhOAExIiY9ATQ2MzERITIWFTEVFAYjMSEiJjUxNTQ2MwM1Iw4BByMVNxUTNSM3PgE3MTY0NTQmJzEuASMiBiMzMCIxIgYHMQ4BBzEXPgE7ATIVDgEHMQcVATsCihkiIhn9dhkiIhkCihkiIhn9dhgjIhkCihkiIhn9dhkiIhm2EA4kFAEvVVYvCg0CAQsJCxkOAgICAQEKFAgIDQQbBRUMAR8CCwlJAwAiGQoZIiIZChki/wAiGQkZIiIYChki/wAiGQoZIiIZChkiAQjlEBsKJxyl/oAkRw4gEgEBAQwWBwgJAQUGBA4JFgsOGhAbC2kMAAUAAACABAADAAATACoAPgBBAEUAABMhMhYVMRUUBiMhIiY1MTU0NjMxASEyFhUxFRQGIzEhOAExIiY9ATQ2MzERITIWFTEVFAYjMSEiJjUxNTQ2MyUXERUzFSM7A4oZIiIZ/HYZIiIZAcAByRkiIhn+NxgjIhkByhkiIhn+NhkiIhn+BcCAgAMAIhkKGSIiGQoZIv8AIhkJGSIiGAoZIv8AIhkKGSIiGQoZIiGhAUCAQAAAAAYAAP/ABAADwAAdADsAPwBDAEcASwAAASIHDgEHBhUUFx4BFxYzMjc+ATc2NTE0Jy4BJyYjESInLgEnJjU0Nz4BNzYzMhceARcWFTEUBw4BBwYjAyEVIREhFSEbARcDNxMXAwIAal1eiygoKCiLXl1qal1eiygoKCiLXl1qUEVGaR4eHh5pRkVQUEVGaR4eHh5pRkVQwAGA/oABgP6AQEo2SkpKN0sDwCgoi15dampdXosoKCgoi15dampdXosoKPyAHh5pRkVQUEVGaR4eHh5pRkVQUEVGaR4eAUBAAQBA/tsB5Rv+GxsB5Rr+GgAAAAUAAACABAADAAATADMASABoAH8AABMhMhYVMRUUBiMhIiY1MTU0NjMxATMyMDEyFhU4ATkBFRQwMRQGIzgBMSMiJjUxNTQ2MzEBITIWFTEVFAYjMSEiJjUxNTQ2MzEBMzIwMTIWFTgBOQEVFDAxFAYjOAExIyImNTE1NDYzMTsBMhYVMRUUBiMxIzgBMSImPQE0NjMxOwOKGSIiGfx2GSIiGQEACQEZIiIZChkiIhn/AAOKGSIiGfx2GSIiGQHACQEZIiIZChkiIhnACRkiIhkJGCMiGQMAIhkKGSIiGQoZIv8AIhkJARkiIhkKGSL/ACIZChkiIhkKGSIBACIZCQEZIiIZChkiIhkJGSIiGAoZIgAAAAEAwACAA0ADAABHAAA3MDIxMjY1OAE5ATU0NjMxIQcOARUUFjMyNjcxNz4BNTQmLwEuASMiBhUUFh8BITgBMSIHDgEHBhU4ATkBFTgBMRQWMzgBOQHoARAYXUMBB4QGBhgQCA8FyAYGBgbIBQ8IEBgGBoT++DIrLEETExcRgBcRoEJehAUOCRAYBwXIBQ8ICA8FyAYGFxEIDgaEExJCKywyoBEXAAAAAAIAQAAAA4ADgAAeADwAAAEHMzIWFRQGKwEiJjUxNTQ2MzIWFTEVNx4BFxUUNgcBNyMiJjU0NjsBMhYVMRUUBiMiJj0BBy4BJzU0BjcDgOdHExoaE7MTGhoTEhvmGCIGBgb8wOdHExoaE7MTGhoTEhvmGCIGBgYDQOYbEhMaGhOzExoaE0fnBSMXAQkPBv0A5hsSExoaE7MTGhoTR+cFIxcBCQ8GAAAAAAEAgAA+A38DQADGAAAlIiMHPgE3Iz4BNzU+AT8BPgE1PAE1MTwBNTQmJxcuAScjLgEnIy4BIyoBIzEqASMiBgc3DgEHMQ4BDwEOARUcARU1FDAxFBYXNR4BFyceAR8BHgEfASciKwEVMBQxFBYXMR4BMzoBOQEhNS4BJxUuAS8BLgEnNS4BNTwBOQE8ATU0NjcVPgE3MT4BNzM+ATMyFhcjHgEXIx4BHwEeARUcARUxMBQVFAYHNw4BBzUOAQ8BDgEHIxUhOgExMjY3MT4BNTA0OQE1AvYXFy0ZLBQBFCIPDhcIAQcJEA8BDikYARk6IAIgSSYBAgEBAgEmSSICIjwZGSgOAQ4PCQgIFw8BDyITARMsFwItFxaKBgYFDwgBAQEZFSYQER4MAQwVBwcICwoJHBERJxYBFjEZGjEYAxgoEQESGwkBCQsIBwEIFA0MHhEBECUVAQEZAQEJDgYFBqgFCRgPDyQUARQuGAMXNhwBAQEBAQEmRyACITcXFiMMDQ0ODQENJBcXNx8CH0YmAQIBAQEdNxoCGi8UARUkDwEOGAkBBkABCA4FBQaRBA8MAQ0eEQESKBcCFjEaAQEBAwEcNBgBFicPDxcICAgICAcYDw8mFgEXNBwBAwEBARoyGAMYKhIBEh4MAQsPBI8GBQUOCAFAAAAAAAIBFQAWA+kCnQALAD0AACUnNyMHJyMXBzM3FwU1Izc+ATc1PAE1NCYnIy4BIyoBIzMqASMiBgc3DgEPARc+ATcxPgEzMTIVDgEHMQcVAsy7q111al2dq2FygAGBj1IQFQMQDQEPJxUCBAIBAQEBEB0NAQwRBAEgBQ0ICBMLOQMSD3PI8OWhoeXwq6uwLYAWNB0BAQMBEyEMCw0IBwEHFAwBGgkNBQUGMxowE7AKAAAAAgEVAMgD6QONAAsAPQAAJSc3IwcnIxcHMzcXATUjNz4BNzU8ATU0JicjLgEjKgEjMTAiIyIGBzMOAQcVFz4BNzE+ATcxMhUOAQc1BxUCzLurXXVqXZ2rYXKAAYGPUhAVAxANARAoFgEDAQIBEB0NAQwRBCAEDQgIFAo5AhMPcsjw5aGh5fCrqwFQLYAWNB0BAQMBEyEMDQ4IBwYUDAEaCA4FBQUBMxsvFAGxCgAAAAAGAEAAQAPAAwAAAwAHAAsADwATABcAABMhFSElIRUhBSEVISUhFSEFIRUhJSEVIUABov5eAd4Bov5e/iIBov5eAd4Bov5e/iIBov5eAd4Bov5eAwDAwMBAwMDAQMDAwAACAIAAQQOAA0AALgBfAAABMhYVMBQVFAYHNQchMhYVFAYjIRceARUcATkBFAYjIiYnNScuATU0NjcxNz4BMwExFx4BFRQGBzEHDgEjMSImNTA0MTQ2NzE3IQYmNTQ2MyEnLgE1PAE5ATQ2MzIWFxUBEBQcCAYOAiwUHBwU/dQOBwccFAsRBmAGCAgGYAYRCwIAYAcHBwdgBhELFBwIBg791hQcHBQCLA4HBxwUCxEGA0AmGgEBDBYKARMmGhomEwkWDAEBGiYKCAGACRcNDBcKgAgL/m2ACRcNDBcKgAgKJRsBDRYJEwEmGhomEwkWDAEBGiYKCAEAAAQBAACAAwEDAAADAAcACwAPAAABJzcXFzcnBwMHFzc3FwcnASkp6ynDKuwpwynrKcMq7CkCACfZJtom2ib+pibaJ9kn2ScAAAIA0P/AAxADDgA5AD0AAAEcARUUFhcxHgEzMjYzIzoBMzI2NxU+ATU8ATUVESMRHAEVFAYHMQ4BIyoBIzEqASMiJjU0NjUxESMDNSEVARIfGx5LKgUKBQEDBwMsTR8cIVgUERIxGwIEAgEEAzVLAVhCAkABTAMHAylJGhgcAR0aARtIKgMGAwEBwP5GAQQCGy8SERRLNQIDAgG2/Lc4OAABAMAAgANAAwAANgAAJSImNTE1NCYjMSEXHgEVFAYjIiYnMScuATU0Nj8BPgEzMhYVFAYHMQchMhceARcWFTEVFAYjMQMYEBdeQv73hAYGGBAIDwXIBgYGBsgFDwgQGAYGhAEIMissQRMTFxGAFxGgQl6EBQ4JEBgHBcgFDwgIDwXIBgYXEQgOBoQTE0EsLDGgERcABgAAAIAEAAMAABIAKAA8AFAAcACEAAATITIWFTEVFAYjISImNTE1NDYzESEyFhUxFRQGIyE4ATEiJj0BNDYzMREhMhYVMRUUBiMxISImNTE1NDYzIzMyFhUxFRQGIzEjIiY1MTU0NjMRMzIwMTIWFTgBOQEVFDAxFAYjOAExIyImNTE1NDYzMREzMhYVMRUUBiMxIyImNTE1NDYz+wLKGSIiGf02GSIiGQLKGSIiGf02GCMiGQLKGSIiGf02GSIiGcAKGSIiGQoZIiIZCQEZIiIZChkiIhkKGSIiGQoZIiIZAwAiGQoZIiIZChki/wAiGQkZIiIYChki/wAiGQoZIiIZChkiIhkKGSIiGQoZIgEAIhkJARkiIhkKGSIBACIZChkiIhkKGSIAAAAAAwBeAE4DrwMdAKcBUgFwAAAlMzUjKgEjIiYnMS4BNTwBNTE0NzY1NjQ1NCYnFS4BJzE1PgE3MT4BNTwBJxU0JyY1PAE1NDY3MT4BMzIWMzEzNSMwIiMiBgczDgEHMQ4BDwEOARUcARU1FBcWFxYXFhUwFDEUBgcxDgEHMQ4BByMOASMqASMxFToBMzIWFyMeARcxHgEXFR4BFzEUBwYHBgcGFRwBFRQWFyceARcxHgEXMx4BMzoBMyMFMzoBMTI2NyM+ATcxPgE3MT4BNTQmNTE0JyYnJicmNTgBMTQ2NxU+ATcxPgE3NT4BMzAyMzE1KgExIiYnFy4BJzEuASc1LgE1OAE5ATQ3Njc2NzY3PAE1NCYnFS4BJzEuAS8BLgEjKgEjMSMVMzoBMzIWFzEeARUUBhUxFAcGFRwBFRQWFyMeARczFQ4BBzEOARUcARU1FBcWFRwBFRQGBzcOASMqASMxIxUlMzc2NzY3MxYXFh8BMyc3IwcGBwYHIyYnJi8BIxcBBC0TAQMCChIGBQYCAgEIBgcWDQ0WBwcHAQICBgUGEgoCAwETLQIBCxYKAQoQBgYKAgEDAwECAQEBAQECAgYEBAsGAQcQCAEBAQEBAQgRBwEHCwUDBgICAQECAQECAQEDBAEDCgYGEAkBCRYLAQIBAQHXLQEBDBYLAgoQBgcJAwMEAQEBAQIBAQIBAgYEBAsHBxAJAgEBAgkQCAEGCwUEBgIBAgECAQEBAQEEAwMJBwYQCQEJFgsBAQEtEwIDAQsRBwUGAQICBwcBCBUNAQ4VCAYHAgIFBgEHEQsBAwIT/mVcOgkJCAoDCwkKCjtgioJcNAgICAkDCwgJCTZgglE0CAcKFgwCBAIkISIoAgQDDx0NAQsPAwMDDwsMHRACBQIBKCIiJAEFAgwWCgYJATUDAwIKBwcSCQEMGg0BAgIBFxITEhESEhQBBgsFBgkEBQcDAwM6AwMDBwUECgUBBQsHFBESEhETExYBAgEOGwwBChIHBwkDAgMDAwIDCQcHEgoMGg4BAgEWExISERISFAYMBgEGCgQFBwIBAgM7AwMBAwcFBAoFAQQMBhUREhIRExMWAQIBDhoNAgsSBwYKAgECAzQIBwkXCwMEAiQhIigCBAMPHQ0KDwMDAw8LDB0QAgQDASgiISQCBAMMFgoBBwg0c2kSERIUFBIREmnj3WMQEBEUFBARD2TaAAAEAAAAQAQAA0AAIwA3AFwAXwAAASEiBw4BBwYVMREUFx4BFxYzMSEyNz4BNzY1MRE0Jy4BJyYjExQGIzEhIiY1MRE0NjMxITIWFTEFMQcOASMwIjkBKgEjIiYnMTU+ATM6ATMxMhYXIxceARUUBgcVJzcnAwD+ADUvLkYUFBQURi4vNQIANS8uRhQUFBRGLi81gEs1/gA1S0s1AgA1S/7pmgYNCAEBAgESGwICGxIBAgEIDgcBmgoNDQqACAsDQBQURi4vNf8ANS8uRhQUFBRGLi81AQA1Ly5GFBT+ADVLSzUBADVLSzWjVgMEGRKqEhkEBFUEEwwLEwQBHgUGAAAACgBAAAADwAOAAA8AJQAzAEEAUgBnAHgAjgCfALUAAAEjIiY1NDYzMTMyFhUUBiMnDgEjIiY1NDY3MTc+ATMyFhUUBgcxASImNTQ2MzIWFTEUBiM1IgYVFBYzMjY1MTQmIzUiJjUxNTQ2MzIWFTEVFAYjBycuATU0NjMyFh8BHgEVFAYjIiYnFxQGIzEjIiY1NDYzMTMyFhUHPgEzMhYVFAYHMQcOASMiJjU0NjcxJTIWFTEVFAYjIiY1MTU0NjM3Fx4BFRQGIyImJzEnLgE1NDYzMhYXA4hwFyEhF3AXISEXmggUCxchCAdQBxULFyEJB/7DRmJiRkZiYkYXISEXFyEhFxchIRcXISEX7k8HCSEXCxUHUAcIIRcLFAgOIRdwFyEhF3AXIQ4IFAsXIQgHUAcVCxchCQcBPRchIRcXISEX7k8HCCEXCxQHUAcIIRcLFAgBiCEXFyEhFxch1gcIIRcLFAhPBwkhFwsVB/5qYkZGYmJGRmLgIRcXISEXFyGoIRdwFyEhF3AXIUJQBxULFyEJB08IFAsXIQgHnhchIRcXISEXngcIIRcLFAhPBwkhFwsVBw4hF3AXISEXcBchQlAHFAsXIQgHTwgUCxchCAcAAAAAAQAAAAEAAIeeNh1fDzz1AAsEAAAAAADWD0mdAAAAANYPSZ3/8f/ABAQDwAAAAAgAAgAAAAAAAAABAAADwP/AAAAEAP/x//wEBAABAAAAAAAAAAAAAAAAAAAALgQAAAAAAAAAAAAAAAIAAAAEAAAABAAAAAQAAAAEAAEZBAAAwAQAAEAEAABABAAAgAQAAEAEAABABAAAQAQAAD8EAAA/BAAAwAQA//EEAABABAAAQAQAAEAEAAAABAAAAAQAAMAEAAGzBAAAAAQAAAAEAAAaBAAAAAQAAAAEAAAABAAAwAQAAEAEAACABAABFQQAARUEAABABAAAgAQAAQAEAADQBAAAwAQAAAAEAABeBAAAAAQAAEAAAAAAAAoAFAAeAGwAugEIAXgBxAImApIDPAOOA8QD+ATUBRAFWAV6BeIGYAbaBxYHcAfYB+YIBgiuCUAJmgoSCpwK8AtEDEAMmAzwDSANnA3ADhAOWg7wELYRNhIgAAAAAQAAAC4BcQAKAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA4ArgABAAAAAAABAAgAAAABAAAAAAACAAcAaQABAAAAAAADAAgAOQABAAAAAAAEAAgAfgABAAAAAAAFAAsAGAABAAAAAAAGAAgAUQABAAAAAAAKABoAlgADAAEECQABABAACAADAAEECQACAA4AcAADAAEECQADABAAQQADAAEECQAEABAAhgADAAEECQAFABYAIwADAAEECQAGABAAWQADAAEECQAKADQAsFJlZGFjdG9yAFIAZQBkAGEAYwB0AG8AclZlcnNpb24gMS4wAFYAZQByAHMAaQBvAG4AIAAxAC4AMFJlZGFjdG9yAFIAZQBkAGEAYwB0AG8AclJlZGFjdG9yAFIAZQBkAGEAYwB0AG8AclJlZ3VsYXIAUgBlAGcAdQBsAGEAclJlZGFjdG9yAFIAZQBkAGEAYwB0AG8AckZvbnQgZ2VuZXJhdGVkIGJ5IEljb01vb24uAEYAbwBuAHQAIABnAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAEkAYwBvAE0AbwBvAG4ALgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") format("truetype"); font-weight: normal; + font-style: normal; } + +[class^="re-icon-"], [class*=" re-icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'Redactor' !important; + speak: none; font-style: normal; -} -/* - Box -*/ + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + +.re-icon-aligncenter:before { + content: "\e900"; } + +.re-icon-alignment:before, +.re-icon-alignleft:before { + content: "\e901"; } + +.re-icon-alignright:before { + content: "\e902"; } + +.re-icon-bold:before { + content: "\e903"; } + +.re-icon-bookmark:before { + content: "\e904"; } + +.re-icon-clips:before { + content: "\e905"; } + +.re-icon-codesnippets:before { + content: "\e906"; } + +.re-icon-deleted:before { + content: "\e907"; } + +.re-icon-expand:before { + content: "\e908"; } + +.re-icon-file:before { + content: "\e909"; } + +.re-icon-fontcolor:before { + content: "\e90a"; } + +.re-icon-fontfamily:before { + content: "\e90b"; } + +.re-icon-fontsize:before { + content: "\e90c"; } + +.re-icon-format:before { + content: "\e90d"; } + +.re-icon-html:before { + content: "\e90e"; } + +.re-icon-imagecenter:before { + content: "\e90f"; } + +.re-icon-imageleft:before { + content: "\e910"; } + +.re-icon-imageright:before { + content: "\e911"; } + +.re-icon-image:before { + content: "\e912"; } + +.re-icon-indent:before { + content: "\e913"; } + +.re-icon-inline:before { + content: "\e914"; } + +.re-icon-italic:before { + content: "\e915"; } + +.re-icon-line:before, +.re-icon-horizontalrule:before { + content: "\e916"; } + +.re-icon-link:before { + content: "\e917"; } + +.re-icon-ol:before, +.re-icon-ordered:before { + content: "\e918"; } + +.re-icon-outdent:before { + content: "\e919"; } + +.re-icon-properties:before { + content: "\e91a"; } + +.re-icon-readmore:before { + content: "\e91b"; } + +.re-icon-redo:before { + content: "\e91c"; } + +.re-icon-retract:before { + content: "\e91d"; } + +.re-icon-specialcharacters:before { + content: "\e91e"; } + +.re-icon-sub:before { + content: "\e91f"; } + +.re-icon-sup:before { + content: "\e920"; } + +.re-icon-table:before { + content: "\e921"; } + +.re-icon-textdirection:before { + content: "\e922"; } + +.re-icon-toggle:before { + content: "\e923"; } + +.re-icon-underline:before { + content: "\e924"; } + +.re-icon-undo:before { + content: "\e925"; } + +.re-icon-ul:before, +.re-icon-lists:before, +.re-icon-unordered:before { + content: "\e926"; } + +.re-icon-variable:before { + content: "\e927"; } + +.re-icon-video:before { + content: "\e928"; } + +.re-icon-widget:before { + content: "\e929"; } + +.redactor-box, +.redactor-box textarea { + z-index: auto; } + .redactor-box { position: relative; - overflow: visible; - margin-bottom: 24px; -} -.redactor-box textarea { - display: block; + box-sizing: border-box; } + .redactor-box.redactor-styles-on { + margin: 0; + padding: 0; + background: #fff; + border: 1px solid rgba(0, 0, 0, 0.075); + border-radius: 3px; + box-shadow: none; } + .redactor-box.redactor-inline { + position: static; } + +.redactor-focus.redactor-styles-on, +.redactor-focus:focus.redactor-styles-on { + border-color: #82b7ec !important; } + +.redactor-over:hover.redactor-styles-on { + border-color: #ee698a !important; } + +.redactor-source-view, +.redactor-source-view.redactor-styles-on { + border-color: #000 !important; } + +.redactor-in { position: relative; + overflow: auto; + white-space: normal; + box-sizing: border-box; } + .redactor-in:focus { + outline: none; } + +.redactor-inline .redactor-in { + overflow: hidden; } + +.redactor-in *, +.redactor-read-only * { + outline: none !important; } + +.redactor-in h1:empty, +.redactor-in h2:empty, +.redactor-in h3:empty, +.redactor-in h4:empty, +.redactor-in h5:empty, +.redactor-in h6:empty, +.redactor-in p:empty, +.redactor-in blockquote:empty { + min-height: 1.5em; } + +.redactor-in strong:empty, .redactor-in b:empty, .redactor-in em:empty, .redactor-in i:empty, .redactor-in span:empty, .redactor-in sup:empty, .redactor-in sub:empty, .redactor-in u:empty, .redactor-in ins:empty { + display: inline-block; + min-width: 1px; + min-height: 1rem; } + +.redactor-in table { + empty-cells: show; } + +.redactor-in li figure { + width: auto; + display: inline-block; margin: 0; - padding: 0; + vertical-align: top; } + +.redactor-in figcaption:focus, +.redactor-in figure code:focus, +.redactor-in figure pre:focus, +.redactor-in table td:focus, +.redactor-in table th:focus { + outline: none; } + +.redactor-in figure[data-redactor-type=line] { + margin-top: 1em; + padding: 6px 0; + vertical-align: baseline; } + .redactor-in figure[data-redactor-type=line] hr { + margin: 0; + height: 3px; + border: none; + background: rgba(0, 0, 0, 0.1); } + +.redactor-script-tag { + display: none !important; } + +.redactor-component { + position: relative; } + +.redactor-component[data-redactor-type="widget"]:before, +.redactor-component[data-redactor-type="video"]:before { width: 100%; - overflow: auto; - outline: none; - border: none; - background-color: #111; - box-shadow: none; - color: #ccc; - font-size: 13px; - font-family: Menlo, Monaco, monospace, sans-serif !important; - resize: none; -} -.redactor-box textarea:focus { - outline: none; -} -.redactor-editor, -.redactor-box { - background: #fff; -} -/* - Z-index setup -*/ -.redactor-editor, -.redactor-box, -.redactor-box textarea { - z-index: auto; -} -.redactor-box-fullscreen { - z-index: 1051; -} -.redactor-toolbar { - z-index: 100; -} -.redactor-dropdown { - z-index: 1052; -} -#redactor-modal-overlay, -#redactor-modal-box, -#redactor-modal { - z-index: 1053; -} -/* - Fullscreen -*/ -body .redactor-box-fullscreen { + height: 100%; + content: ""; + display: block; + position: absolute; + z-index: 1; } + +.redactor-component[data-redactor-type=image], +.redactor-component[data-redactor-type=widget] { + clear: both; } + +.redactor-component[data-redactor-type=variable] { + white-space: nowrap; + background: rgba(0, 125, 255, 0.75); + color: #fff; + display: inline-block; + padding: 3px 6px; + line-height: 1; + border-radius: 4px; + cursor: pointer; } + +.redactor-component-active { + outline: 5px solid rgba(0, 125, 255, 0.5) !important; } + +.redactor-component-active[data-redactor-type=image] { + outline: none !important; } + .redactor-component-active[data-redactor-type=image] img { + outline: 5px solid rgba(0, 125, 255, 0.5) !important; } + +.redactor-component-active[data-redactor-type=variable] { + outline: none !important; + background: #ee698a; } + +.redactor-component-active[data-redactor-type=video] { + outline: none !important; } + .redactor-component-active[data-redactor-type=video] iframe { + outline: 5px solid rgba(0, 125, 255, 0.5) !important; } + +.redactor-blur.redactor-styles-on .redactor-component-active { + outline: 5px solid #ddd !important; } + .redactor-blur.redactor-styles-on .redactor-component-active[data-redactor-type=image] { + outline: none !important; } + .redactor-blur.redactor-styles-on .redactor-component-active[data-redactor-type=image] img { + outline: 5px solid #ddd !important; } + .redactor-blur.redactor-styles-on .redactor-component-active[data-redactor-type=video] { + outline: none !important; } + .redactor-blur.redactor-styles-on .redactor-component-active[data-redactor-type=video] iframe { + outline: 5px solid #ddd !important; } + .redactor-blur.redactor-styles-on .redactor-component-active[data-redactor-type=variable] { + outline: none !important; + background: #ddd; } + +.redactor-component-caret { + position: absolute; + left: -9999px; } + +.redactor-textnodes-wrapper { + display: inline-block; } + +#redactor-image-resizer { + position: absolute; + z-index: 1050; + background-color: rgba(0, 125, 255, 0.9); + width: 13px; + height: 13px; + border: 1px solid #fff; + cursor: move; + cursor: nwse-resize; } + +.redactor-file-item { + display: inline-block; + line-height: 1; + padding: 4px 12px; + border-radius: 16px; + border: 1px solid rgba(0, 0, 0, 0.2); } + +.redactor-file-remover { + margin-left: 2px; + position: relative; + right: -3px; + display: inline-block; + padding: 0 3px; + cursor: pointer; + opacity: .5; } + .redactor-file-remover:hover { + opacity: 1; } + +#redactor-overlay { position: fixed; + z-index: 1051; top: 0; left: 0; + right: 0; + bottom: 0; + background-color: rgba(16, 16, 18, 0.3); } + #redactor-overlay > .redactor-close { + position: fixed; + top: 1rem; + right: 1rem; } + +.redactor-source, +.redactor-source:hover, +.redactor-source:focus { + text-align: left; + box-sizing: border-box; + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; width: 100%; -} -/* - Utils -*/ -.redactor-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -/* - Editor -*/ -.redactor-editor { - position: relative; - overflow: auto; - margin: 0 !important; - padding: 20px; - min-height: 80px; - outline: none; - white-space: normal; - border: 1px solid #eee; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - font-size: 14px; - line-height: 1.6em; -} -.redactor-editor:focus { + display: block; + margin: 0; + border: none; + box-shadow: none; + border-radius: 0; + background: #252525; + color: #ccc; + font-size: 15px; outline: none; -} -.toolbar-fixed-box + .redactor-editor { - padding-top: 32px !important; -} -/* - Placeholder -*/ -.redactor-placeholder:after { + padding: 10px 18px 20px 18px; + line-height: 1.5; + resize: vertical; } + +.redactor-box[dir="rtl"] .redactor-source { + direction: ltr; } + +.redactor-placeholder:before { position: absolute; - top: 20px; - left: 20px; content: attr(placeholder); - display: block; - /* For Firefox */ - color: #999 !important; - font-weight: normal !important; -} -/* - Toolbar -*/ -.redactor-toolbar { + color: rgba(0, 0, 0, 0.4); + font-weight: normal; } + +.redactor-in figcaption[placeholder]:empty:before { + content: attr(placeholder); + color: rgba(0, 0, 0, 0.4); + font-weight: normal; } + +.redactor-in figcaption[placeholder]:empty:focus:before { + content: ""; } + +.redactor-statusbar { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + margin: 0; + padding: 8px 10px; + position: relative; + overflow: hidden; + list-style: none; + background: #f8f8f8; + box-sizing: border-box; + border: none; } + .redactor-statusbar li { + float: left; + font-size: 12px; + color: rgba(0, 0, 0, 0.5); + padding: 0 10px; + line-height: 16px; + border-right: 1px solid rgba(0, 0, 0, 0.1); } + .redactor-statusbar li:last-child { + border-right-color: transparent; } + .redactor-statusbar a { + color: rgba(0, 0, 0, 0.5); + text-decoration: underline; } + .redactor-statusbar a:hover { + color: #f03c69; + text-decoration: underline; } + .redactor-statusbar:empty { + display: none; } + +.redactor-toolbar-wrapper { + position: relative; } + +.redactor-toolbar, +.redactor-air { + z-index: 100; + font-family: "Trebuchet MS", "Helvetica Neue", Helvetica, Tahoma, sans-serif; position: relative; - top: 0; - left: 0; margin: 0 !important; - padding: 0 !important; + padding: 0; list-style: none !important; - font-size: 14px !important; line-height: 1 !important; - background: #fff; + background: none; border: none; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); -} -.redactor-toolbar:after { - content: ""; - display: table; - clear: both; -} -.redactor-toolbar.redactor-toolbar-overflow { - overflow-y: auto; - height: 29px; - white-space: nowrap; -} -.redactor-toolbar.redactor-toolbar-external { - z-index: 999; - box-shadow: none; - border: 1px solid rgba(0, 0, 0, 0.1); -} -.redactor-toolbar li { - vertical-align: top; + box-sizing: border-box; } + +.redactor-box.redactor-styles-on .redactor-toolbar { + padding: 18px 16px 0 16px; } + +.redactor-toolbar a, +.redactor-air a { display: inline-block; - margin: 0 !important; - padding: 0 !important; - outline: none; - list-style: none !important; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -.redactor-toolbar li a { - display: block; - color: #333; + box-sizing: border-box; + font-size: 14px; text-align: center; - padding: 9px 10px; + padding: 10px 15px 9px 15px; + cursor: pointer; outline: none; border: none; + vertical-align: middle; text-decoration: none; - cursor: pointer; zoom: 1; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -.redactor-toolbar li a:hover { - outline: none; - background-color: #1f78d8; - color: #fff; -} -.redactor-toolbar li a:hover i:before { - color: #fff; -} -.redactor-toolbar li a:active, -.redactor-toolbar li a.redactor-act { - outline: none; - background-color: #ccc; - color: #444; -} -.redactor-toolbar li a.redactor-btn-image { - width: 14px; - height: 14px; - background-position: center center; - background-repeat: no-repeat; -} -.redactor-toolbar li a.fa-redactor-btn { - display: inline-block; - padding: 9px 10px 8px 10px; - line-height: 1; -} -.redactor-toolbar li a.redactor-button-disabled { - filter: alpha(opacity=30); - -moz-opacity: 0.3; - opacity: 0.3; -} -.redactor-toolbar li a.redactor-button-disabled:hover { - color: #333; + position: relative; + color: rgba(0, 0, 0, 0.85); + border-radius: 2px; + background: rgba(255, 255, 255, 0.97); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.22); + margin-right: 5px; + margin-bottom: 4px; } + +.redactor-toolbar a.re-button-icon, +.redactor-air a.re-button-icon { + font-size: 16px; + padding: 9px 15px 6px 15px; } + +.redactor-toolbar a:hover, +.redactor-air a:hover { outline: none; - background-color: transparent !important; - cursor: default; -} -.redactor-toolbar li a.redactor-button-focus { color: #fff; + background: #449aef; } + +.redactor-toolbar a.redactor-button-active { + background: rgba(245, 245, 245, 0.95); + color: rgba(0, 0, 0, 0.4); } + +.redactor-toolbar a.redactor-button-disabled, +.redactor-air a.redactor-button-disabled { + opacity: 0.3; } + .redactor-toolbar a.redactor-button-disabled:hover, + .redactor-air a.redactor-button-disabled:hover { + color: #333; + outline: none; + background-color: transparent !important; + cursor: default; } + +.redactor-source-view .redactor-toolbar { + background: #252525; } + +.redactor-source-view .redactor-toolbar a { background: #000; -} -/* - CodeMirror -*/ -.redactor-box .CodeMirror { + color: #fff; } + .redactor-source-view .redactor-toolbar a:hover { + background: #449aef; } + +.redactor-source-view .redactor-toolbar a.redactor-button-disabled:hover { + color: #fff !important; + background-color: #000 !important; } + +.re-button-tooltip { display: none; -} -/* - Icons -*/ -.re-icon { - font-family: 'RedactorFont'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; + position: absolute; + white-space: nowrap; + top: 0; + z-index: 1052; + background: rgba(0, 0, 0, 0.9); + border-radius: 3px; + padding: 5px 9px; + color: rgba(255, 255, 255, 0.8); + font-size: 12px; line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.re-icon i:before { - position: relative; - font-size: 14px; -} -.re-video:before { - content: "\e600"; -} -.re-unorderedlist:before { - content: "\e601"; -} -.re-undo:before { - content: "\e602"; -} -.re-underline:before { - content: "\e603"; -} -.re-textdirection:before { - content: "\e604"; -} -.re-fontcolor:before { - content: "\e605"; -} -.re-table:before { - content: "\e606"; -} -.re-redo:before { - content: "\e607"; -} -.re-quote:before { - content: "\e608"; -} -.re-outdent:before { - content: "\e609"; -} -.re-orderedlist:before { - content: "\e60a"; -} -.re-link:before { - content: "\e60b"; -} -.re-horizontalrule:before { - content: "\e60c"; -} -.re-italic:before { - content: "\e60d"; -} -.re-indent:before { - content: "\e60e"; -} -.re-image:before { - content: "\e60f"; -} -.re-fullscreen:before { - content: "\e610"; -} -.re-normalscreen:before { - content: "\e611"; -} -.re-formatting:before { - content: "\e612"; -} -.re-fontsize:before { - content: "\e613"; -} -.re-fontfamily:before { - content: "\e614"; -} -.re-deleted:before { - content: "\e615"; -} -.re-html:before { - content: "\e616"; -} -.re-clips:before { - content: "\e617"; -} -.re-bold:before { - content: "\e618"; -} -.re-backcolor:before { - content: "\e619"; -} -.re-file:before { - content: "\e61a"; -} -.re-alignright:before { - content: "\e61b"; -} -.re-alignment:before, -.re-alignleft:before { - content: "\e61c"; -} -.re-alignjustify:before { - content: "\e61d"; -} -.re-aligncenter:before { - content: "\e61e"; -} -.re-gallery:before { - content: "\e61f"; -} -/* - Toolbar tooltip -*/ -.redactor-toolbar-tooltip { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; } + .re-button-tooltip:after { + bottom: 100%; + left: 50%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-color: none; + border-bottom-color: rgba(0, 0, 0, 0.9); + border-width: 4px; + margin-left: -4px; } + +.redactor-toolbar-wrapper-air { position: absolute; - z-index: 1054; - text-align: center; + z-index: 100; } + +.redactor-air { + padding: 6px 3px 2px 8px; + max-width: 576px; + min-width: 200px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.97); } + +.redactor-air a { + background: rgba(37, 37, 37, 0.95); + box-shadow: none; + color: rgba(255, 255, 255, 0.9); } + +.redactor-air a:hover { + background: #3d79f2; } + +.redactor-air a.redactor-button-active { + background-color: rgba(255, 255, 255, 0.15); + color: #fff; } + +.redactor-air a.redactor-button-disabled:hover { + color: #fff; } + +.redactor-air-helper { + position: absolute; + right: 0; top: 0; - left: 0; - background: #000; - color: #fff; - padding: 5px 8px; line-height: 1; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - font-size: 12px; - border-radius: 2px; -} -/* - Dropdown -*/ -.redactor-dropdown { + font-size: 15px; + color: #000; + background: rgba(255, 255, 255, 0.85); + border-bottom-left-radius: 4px; + padding: 7px 10px 6px 10px; + cursor: pointer; } + .redactor-air-helper:hover { + background: #fff; } + +.redactor-voice-label { + display: none; } + +.redactor-context-toolbar { position: absolute; - top: 28px; + top: 0; left: 0; - padding: 0; - min-width: 220px; - max-height: 254px; - overflow: auto; + z-index: 1051; + background-color: rgba(0, 0, 0, 0.95); + color: #555; + border-radius: 4px; + padding: 6px 18px 7px 18px; + line-height: 1.5; + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; } + .redactor-context-toolbar a { + font-size: 12px; + color: #ccc; + text-decoration: none; + display: inline-block; + padding: 2px 0 1px 12px; } + .redactor-context-toolbar a:first-child { + padding-left: 0; } + .redactor-context-toolbar a i { + position: relative; + top: 3px; + font-size: 16px; } + .redactor-context-toolbar a:before { + content: ''; + padding-left: 10px; + border-left: 1px solid rgba(255, 255, 255, 0.3); } + .redactor-context-toolbar a:hover { + color: #fff; } + .redactor-context-toolbar a:first-child:before { + padding-left: 0; + border-left: none; } + +.redactor-context-toolbar[dir="rtl"] a { + padding: 2px 12px 1px 0; } + +.redactor-context-toolbar[dir="rtl"] a:first-child { + padding-right: 0; } + +.redactor-context-toolbar[dir="rtl"] a:before { + padding-left: 0px; + padding-right: 10px; + border-right: 1px solid rgba(255, 255, 255, 0.3); + border-left: none; } + +.redactor-context-toolbar[dir="rtl"] a:first-child:before { + padding-right: 0; + border-right: none; } + +.redactor-dropdown { + font-family: "Trebuchet MS", "Helvetica Neue", Helvetica, Tahoma, sans-serif; + display: none; + position: absolute; + z-index: 1051; background-color: #fff; - box-shadow: 0 1px 7px rgba(0, 0, 0, 0.25); + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); + border-radius: 2px; + width: 264px; + max-height: 250px; + margin: 0; + margin-top: -1px; + overflow: auto; + font-size: 15px; + padding: 0; } + .redactor-dropdown a span { + display: inline-block; + line-height: 1; + padding: 2px 4px; + border-radius: 3px; } + .redactor-dropdown a { + display: block; + text-decoration: none; + padding: 10px 8px; + white-space: nowrap; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } + .redactor-dropdown a:last-child { + border-bottom-color: transparent; } + .redactor-dropdown a { + color: #000; } + .redactor-dropdown a:hover { + color: #fff !important; + background-color: #449aef !important; } + .redactor-dropdown a.redactor-dropdown-item-disabled { + color: rgba(0, 0, 0, 0.4); + background: #fff; } + +.redactor-dropdown-cells { + margin: 10px auto; } + .redactor-dropdown-cells a, + .redactor-dropdown-cells span { + float: left; + cursor: pointer; + box-sizing: border-box; + text-align: center; + padding: 0; + margin: 0; + font-size: 14px; } + +.redactor-dropdown-selector { + display: flex; + text-align: center; } + .redactor-dropdown-selector span { + flex-grow: 1; + font-size: 12px; + padding: 8px; + cursor: pointer; } + .redactor-dropdown-selector span:hover { + background: #eee; } + .redactor-dropdown-selector span.active { + cursor: text; + color: rgba(0, 0, 0, 0.3); + background: #eee; } + +.redactor-dropdown-format .redactor-dropdown-item-blockquote { + color: rgba(0, 0, 0, 0.4); + font-style: italic; } + +.redactor-dropdown-format .redactor-dropdown-item-pre { + font-family: monospace, sans-serif; } + +.redactor-dropdown-format .redactor-dropdown-item-h1 { + font-size: 40px; + font-weight: bold; + line-height: 32px; } + +.redactor-dropdown-format .redactor-dropdown-item-h2 { + font-size: 32px; + font-weight: bold; + line-height: 32px; } + +.redactor-dropdown-format .redactor-dropdown-item-h3 { + font-size: 24px; + font-weight: bold; + line-height: 24px; } + +.redactor-dropdown-format .redactor-dropdown-item-h4 { + font-size: 21px; + font-weight: bold; + line-height: 24px; } + +.redactor-dropdown-format .redactor-dropdown-item-h5 { + font-size: 18px; + font-weight: bold; + line-height: 24px; } + +.redactor-dropdown-format .redactor-dropdown-item-h6 { font-size: 14px; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - line-height: 1.6em; -} -.redactor-dropdown a { - display: block; - padding: 10px 15px; + text-transform: uppercase; + font-weight: bold; + line-height: 24px; } + +#redactor-modal { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + overflow-x: hidden; + overflow-y: auto; + z-index: 1051; + font-family: "Trebuchet MS", "Helvetica Neue", Helvetica, Tahoma, sans-serif; + line-height: 24px; } + +.redactor-modal { + position: relative; + margin: 16px auto; + padding: 0; + background: #fff; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07), 0 2px 15px rgba(80, 80, 80, 0.25); + border-radius: 3px; + color: #000; } + .redactor-modal form { + margin: 0; + padding: 0; + box-sizing: border-box; } + .redactor-modal input, + .redactor-modal select, + .redactor-modal textarea { + box-sizing: border-box; + display: block; + width: 100%; + font-family: inherit; + font-size: 16px; + height: 40px; + outline: none; + vertical-align: middle; + background-color: #fff; + border: 1px solid #cacfd4; + border-radius: 0.1875em; + box-shadow: none; + padding: 0 .5em; } + .redactor-modal textarea { + padding: .5em; + height: auto; + line-height: 1.5; + vertical-align: top; } + .redactor-modal select { + -webkit-appearance: none; + background-image: url('data:image/svg+xml;utf8,'); + background-repeat: no-repeat; + background-position: right .65em center; + padding-right: 28px; } + .redactor-modal select[multiple] { + background-image: none; + height: auto; + padding: .5em .75em; } + .redactor-modal input[type="file"] { + width: auto; + border: none; + padding: 0; + height: auto; + background: none; + box-shadow: none; + display: inline-block; } + .redactor-modal input[type="radio"], + .redactor-modal input[type="checkbox"] { + display: inline-block; + width: auto; + height: auto; + padding: 0; + vertical-align: middle; + position: relative; + bottom: 0.15rem; + font-size: 115%; + margin-right: 3px; } + .redactor-modal .form-item { + margin-bottom: 20px; } + .redactor-modal .form-item:last-child { + margin-bottom: 0; } + .redactor-modal fieldset { + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 3px; + padding: 16px; + padding-bottom: 20px; + margin-bottom: 20px; } + .redactor-modal fieldset .form-item { + margin-bottom: 12px; } + .redactor-modal label { + display: block; + color: #555; + margin-bottom: 0.25em; + font-size: 14px; } + .redactor-modal label .desc, + .redactor-modal label .success, + .redactor-modal label .error { + text-transform: none; + font-weight: normal; } + .redactor-modal label.checkbox { + font-size: 16px; + line-height: 1.5; + cursor: pointer; + color: inherit; } + .redactor-modal .form-checkboxes label.checkbox { + display: inline-block; + margin-right: 1em; } + .redactor-modal input:hover, + .redactor-modal textarea:hover, + .redactor-modal select:hover { + outline: none; + background-color: #fff; + border-color: #969fa9; + box-shadow: none; } + .redactor-modal input:focus, + .redactor-modal textarea:focus, + .redactor-modal select:focus { + transition: all linear .2s; + outline: none; + background-color: #fff; + border-color: rgba(0, 125, 255, 0.5); + box-shadow: 0 0 3px rgba(0, 125, 255, 0.5); } + .redactor-modal input.error, + .redactor-modal textarea.error, + .redactor-modal select.error { + background-color: rgba(255, 50, 101, 0.1); + border: 1px solid #ff7f9e; } + .redactor-modal input.error:focus, + .redactor-modal textarea.error:focus, + .redactor-modal select.error:focus { + border-color: #ff3265; + box-shadow: 0 0 1px #ff3265; } + .redactor-modal input.success, + .redactor-modal textarea.success, + .redactor-modal select.success { + background-color: rgba(47, 196, 182, 0.1); + border: 1px solid #65dacf; } + .redactor-modal input.success:focus, + .redactor-modal textarea.success:focus, + .redactor-modal select.success:focus { + border-color: #2fc4b6; + box-shadow: 0 0 1px #2fc4b6; } + .redactor-modal input:disabled, .redactor-modal input:disabled:hover, .redactor-modal input.disabled, .redactor-modal input.disabled:hover, + .redactor-modal textarea:disabled, + .redactor-modal textarea:disabled:hover, + .redactor-modal textarea.disabled, + .redactor-modal textarea.disabled:hover, + .redactor-modal select:disabled, + .redactor-modal select:disabled:hover, + .redactor-modal select.disabled, + .redactor-modal select.disabled:hover { + resize: none; + opacity: .6; + cursor: default; + font-style: italic; + color: rgba(0, 0, 0, 0.5); + border: 1px solid #cacfd4; + box-shadow: none; + background-color: #fff; } + .redactor-modal .req { + position: relative; + top: 1px; + font-weight: bold; + color: #ff3265; + font-size: 110%; } + .redactor-modal .desc { + color: rgba(51, 51, 51, 0.5); + font-size: 12px; } + .redactor-modal span.desc { + margin-left: 0.25em; } + .redactor-modal div.desc { + margin-top: 0.25em; } + .redactor-modal span.success, + .redactor-modal span.error { + font-size: 12px; + margin-left: 0.25em; } + .redactor-modal div.desc { + margin-bottom: -0.5em; } + .redactor-modal .redactor-close { + position: absolute; + top: 16px; + right: 12px; + font-size: 30px; + line-height: 30px; + padding: 0px 4px; + color: #000; + opacity: .3; + cursor: pointer; } + .redactor-modal .redactor-close:hover { + opacity: 1; } + .redactor-modal .redactor-close:before { + content: '\00d7'; } + .redactor-modal button { + display: inline-flex; + align-items: center; + text-decoration: none; + text-align: center; + font-family: inherit; + font-size: 15px; + font-weight: 500; + color: #007dff; + background-color: #fff; + border-radius: 3px; + border: 2px solid #007dff; + min-height: 40px; + outline: none; + padding: 0.5em 1.25em; + cursor: pointer; + line-height: 1.2; + vertical-align: middle; + -webkit-appearance: none; } + .redactor-modal button:hover { + outline: none; + text-decoration: none; + background: none; + color: rgba(0, 125, 255, 0.6); + border-color: rgba(0, 125, 255, 0.5); } + .redactor-modal button.redactor-button-secondary { + border-color: #2a2e34; + color: #2a2e34; } + .redactor-modal button.redactor-button-secondary:hover { + color: rgba(42, 46, 52, 0.6); + border-color: rgba(42, 46, 52, 0.5); } + .redactor-modal button.redactor-button-danger, + .redactor-modal button.redactor-button-unstyled { + background: none; + border-color: transparent; + color: rgba(42, 46, 52, 0.6); } + .redactor-modal button.redactor-button-danger:hover, + .redactor-modal button.redactor-button-unstyled:hover { + background: none; + border-color: transparent; + color: #ff3265; + text-decoration: underline; } + .redactor-modal .redactor-modal-group:after { + content: ""; + display: table; + clear: both; } + .redactor-modal .redactor-modal-side { + float: left; + width: 30%; + margin-right: 4%; } + .redactor-modal .redactor-modal-side img { + max-width: 100%; + height: auto; + display: block; } + .redactor-modal .redactor-modal-area { + float: left; + width: 66%; } + +.redactor-modal[dir="rtl"] .redactor-close { + left: 12px; + right: auto; } + +.redactor-modal[dir="rtl"] textarea { + direction: ltr; + text-align: left; } + +.redactor-modal[dir="rtl"] .redactor-modal-footer button.redactor-button-unstyled { + float: left; + margin-left: 0; } + +.redactor-modal-header { + padding: 20px; + font-size: 18px; + line-height: 24px; + font-weight: bold; color: #000; - text-decoration: none; - border-bottom: 1px solid rgba(0, 0, 0, 0.07); -} -.redactor-dropdown a:last-child { - border-bottom: none; -} -.redactor-dropdown a:hover { - background-color: #1f78d8; - color: #fff !important; - text-decoration: none; -} -.redactor-dropdown a.selected { - background-color: #000; - color: #fff; -} -.redactor-dropdown a.redactor-dropdown-link-inactive, -.redactor-dropdown a.redactor-dropdown-link-inactive:hover { - background: none; - cursor: default; - color: #000 !important; - filter: alpha(opacity=40); - -moz-opacity: 0.4; - opacity: 0.4; -} -.redactor-dropdown a.redactor-dropdown-link-selected { - color: #fff; - background: #000; -} -/* - IMAGE BOX -*/ -#redactor-image-box { + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } + .redactor-modal-header:empty { + display: none; } + +.redactor-modal-body { + padding: 32px 48px; + padding-bottom: 40px; } + +.redactor-modal-footer { + padding: 24px; + border-top: 1px solid rgba(0, 0, 0, 0.05); + overflow: hidden; } + .redactor-modal-footer button { + margin-right: 4px; } + .redactor-modal-footer button.redactor-button-unstyled { + margin-right: 0; + float: right; } + .redactor-modal-footer:empty { + display: none; } + +.redactor-modal-tabs { + display: flex; + border-bottom: 2px solid rgba(0, 0, 0, 0.05); + margin-bottom: 1em; } + .redactor-modal-tabs a { + font-size: 15px; + padding: 2px 0; + text-decoration: none; + color: rgba(0, 0, 0, 0.5); + border-bottom: 2px solid transparent; + margin-bottom: -2px; + margin-right: 14px; } + .redactor-modal-tabs a:hover { + transition: all linear .2s; } + .redactor-modal-tabs a:hover, + .redactor-modal-tabs a.active { + font-weight: 500; + color: #007dff; + border-bottom-color: #007dff; } + +.redactor-styles { + margin: 0; + padding: 16px 18px; + color: #333; + font-family: "Trebuchet MS", "Helvetica Neue", Helvetica, Tahoma, sans-serif; + font-size: 1em; + line-height: 1.5; + box-sizing: border-box; } + .redactor-styles *, + .redactor-styles *:before, + .redactor-styles *:after { + box-sizing: inherit; } + .redactor-styles[dir="rtl"] { + direction: rtl; + unicode-bidi: embed; } + .redactor-styles[dir="rtl"] ul li, + .redactor-styles[dir="rtl"] ol li { + text-align: right; } + .redactor-styles[dir="rtl"] ul, + .redactor-styles[dir="rtl"] ol, + .redactor-styles[dir="rtl"] ul ul, + .redactor-styles[dir="rtl"] ol ol, + .redactor-styles[dir="rtl"] ul ol, + .redactor-styles[dir="rtl"] ol ul { + margin: 0 1.5em 0 0; } + .redactor-styles[dir="rtl"] figcaption { + text-align: right; } + .redactor-styles a, + .redactor-styles a:hover { + color: #3397ff; } + .redactor-styles p, + .redactor-styles dl, + .redactor-styles blockquote, + .redactor-styles hr, + .redactor-styles pre, + .redactor-styles table, + .redactor-styles figure, + .redactor-styles address { + padding: 0; + margin: 0; + margin-bottom: 1em; } + .redactor-styles ul, + .redactor-styles ol { + padding: 0; } + .redactor-styles ul, + .redactor-styles ul ul, + .redactor-styles ul ol, + .redactor-styles ol, + .redactor-styles ol ul, + .redactor-styles ol ol { + margin: 0 0 0 1.5em; } + .redactor-styles ul li, + .redactor-styles ol li { + text-align: left; } + .redactor-styles ol ol li { + list-style-type: lower-alpha; } + .redactor-styles ol ol ol li { + list-style-type: lower-roman; } + .redactor-styles ul, + .redactor-styles ol { + margin-bottom: 1em; } + .redactor-styles h1, + .redactor-styles h2, + .redactor-styles h3, + .redactor-styles h4, + .redactor-styles h5, + .redactor-styles h6 { + font-weight: bold; + color: #111; + text-rendering: optimizeLegibility; + margin: 0; + padding: 0; + margin-bottom: 0.5em; + line-height: 1.2; } + .redactor-styles h1 { + font-size: 2.0736em; } + .redactor-styles h2 { + font-size: 1.728em; } + .redactor-styles h3 { + font-size: 1.44em; } + .redactor-styles h4 { + font-size: 1.2em; } + .redactor-styles h5 { + font-size: 1em; } + .redactor-styles h6 { + font-size: 0.83333em; + text-transform: uppercase; + letter-spacing: .035em; } + .redactor-styles blockquote { + font-style: italic; + color: rgba(0, 0, 0, 0.5); + border: none; } + .redactor-styles table { + width: 100%; } + .redactor-styles time, .redactor-styles small, .redactor-styles var, .redactor-styles code, .redactor-styles kbd, .redactor-styles mark { + display: inline-block; + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + font-size: 87.5%; + line-height: 1; + color: rgba(51, 51, 51, 0.9); } + .redactor-styles var, .redactor-styles cite { + opacity: .6; } + .redactor-styles var { + font-style: normal; } + .redactor-styles dfn, + .redactor-styles abbr { + text-transform: uppercase; } + .redactor-styles dfn[title], + .redactor-styles abbr[title] { + text-decoration: none; + border-bottom: 1px dotted rgba(0, 0, 0, 0.5); + cursor: help; } + .redactor-styles code, .redactor-styles kbd { + position: relative; + top: -1px; + padding: 0.25em; + padding-bottom: 0.2em; + border-radius: 2px; } + .redactor-styles code { + background-color: #eff1f2; } + .redactor-styles mark { + border-radius: 2px; + padding: 0.125em 0.25em; + background-color: #fdb833; } + .redactor-styles kbd { + border: 1px solid #e5e7e9; } + .redactor-styles sub, + .redactor-styles sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; } + .redactor-styles sub { + bottom: -0.25em; } + .redactor-styles sup { + top: -0.5em; } + .redactor-styles pre { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + font-size: .9em; } + .redactor-styles pre, + .redactor-styles pre code { + background-color: #f6f7f8; + padding: 0; + top: 0; + display: block; + line-height: 1.5; + color: rgba(51, 51, 51, 0.85); + overflow: none; + white-space: pre-wrap; } + .redactor-styles pre { + padding: 1rem; } + .redactor-styles table { + border-collapse: collapse; + max-width: 100%; + width: 100%; } + .redactor-styles table caption { + text-transform: uppercase; + padding: 0; + color: rgba(0, 0, 0, 0.5); + font-size: 11px; } + .redactor-styles table th, + .redactor-styles table td { + border: 1px solid #eee; + padding: 16px; + padding-bottom: 15px; } + .redactor-styles table tfoot th, + .redactor-styles table tfoot td { + color: rgba(0, 0, 0, 0.5); } + .redactor-styles img, + .redactor-styles video, + .redactor-styles audio, + .redactor-styles embed, + .redactor-styles object { + max-width: 100%; } + .redactor-styles img, + .redactor-styles video, + .redactor-styles embed, + .redactor-styles object { + height: auto !important; } + .redactor-styles img { + vertical-align: middle; + -ms-interpolation-mode: bicubic; } + .redactor-styles figcaption { + display: block; + opacity: .6; + font-size: 12px; + font-style: italic; + text-align: left; } + +.upload-redactor-box { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + box-sizing: border-box; + border: 5px dashed rgba(0, 125, 255, 0.15); position: relative; - max-width: 100%; - display: inline-block; - line-height: 0; - outline: 1px dashed rgba(0, 0, 0, 0.6); -} -#redactor-image-editter { - position: absolute; - z-index: 5; - top: 50%; - left: 50%; - margin-top: -11px; - margin-left: -18px; - line-height: 1; - background-color: #000; - color: #fff; - font-size: 11px; - padding: 7px 10px; + width: 100%; + min-height: 220px; + background: #fff; cursor: pointer; -} -#redactor-image-resizer { - position: absolute; - z-index: 2; - line-height: 1; - cursor: nw-resize; - bottom: -4px; - right: -5px; - border: 1px solid #fff; - background-color: #000; - width: 8px; - height: 8px; -} -/* - LINK TOOLTIP -*/ -.redactor-link-tooltip { - position: absolute; - z-index: 99; - padding: 10px; - line-height: 1; - display: inline-block; - background-color: #000; - color: #555 !important; -} -.redactor-link-tooltip, -.redactor-link-tooltip a { - font-size: 12px; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; -} -.redactor-link-tooltip a { - color: #ccc; - margin: 0 5px; - text-decoration: none; -} -.redactor-link-tooltip a:hover { - color: #fff; -} -/* - DROPAREA -*/ -#redactor-droparea { - position: relative; overflow: hidden; - padding: 140px 20px; - border: 3px dashed rgba(0, 0, 0, 0.1); -} -#redactor-droparea.drag-hover { - background: rgba(200, 222, 250, 0.75); -} -#redactor-droparea.drag-drop { - background: rgba(250, 248, 200, 0.5); -} -#redactor-droparea-placeholder { - text-align: center; - font-size: 12px; - color: rgba(0, 0, 0, 0.7); -} -/* - PROGRESS -*/ + text-align: center; } + +.upload-redactor-placeholder { + font-size: 15px; + line-height: 1.5; + color: rgba(0, 0, 0, 0.3); + font-style: italic; } + +.upload-redactor-hover { + background-color: rgba(0, 125, 255, 0.075); } + +.upload-redactor-error { + background-color: rgba(255, 50, 101, 0.075); } + +.upload-redactor-box-hover { + outline: 5px dashed rgba(0, 125, 255, 0.3); } + +.upload-redactor-box-error { + outline: 5px dashed rgba(255, 50, 101, 0.3); } + +.redactor-structure h1, .redactor-structure h2, .redactor-structure h3, .redactor-structure h4, .redactor-structure h5, .redactor-structure h6, .redactor-structure div { + position: relative; } + .redactor-structure h1:before, .redactor-structure h2:before, .redactor-structure h3:before, .redactor-structure h4:before, .redactor-structure h5:before, .redactor-structure h6:before, .redactor-structure div:before { + width: 24px; + position: absolute; + font-size: 10px; + font-weight: normal; + opacity: .3; + left: -26px; + top: 50%; + margin-top: -7px; + text-align: right; } + +.redactor-structure h1:before { + content: "h1"; } + +.redactor-structure h2:before { + content: "h2"; } + +.redactor-structure h3:before { + content: "h3"; } + +.redactor-structure h4:before { + content: "h4"; } + +.redactor-structure h5:before { + content: "h5"; } + +.redactor-structure h6:before { + content: "h6"; } + +.redactor-structure div:before { + content: "div"; } + #redactor-progress { position: fixed; top: 0; left: 0; width: 100%; z-index: 1000000; - height: 10px; -} + height: 10px; } + #redactor-progress span { - display: block; - width: 100%; - height: 100%; - background-color: #3d58a8; background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); -webkit-animation: progress-bar-stripes 2s linear infinite; -o-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; - background-size: 40px 40px; -} + display: block; + width: 100%; + height: 100%; + background-color: #007dff; + background-size: 40px 40px; } + @-webkit-keyframes progress-bar-stripes { from { - background-position: 40px 0; - } + background-position: 40px 0; } to { - background-position: 0 0; - } -} + background-position: 0 0; } } + @-o-keyframes progress-bar-stripes { from { - background-position: 40px 0; - } + background-position: 40px 0; } to { - background-position: 0 0; - } -} + background-position: 0 0; } } + @keyframes progress-bar-stripes { from { - background-position: 40px 0; - } + background-position: 40px 0; } to { - background-position: 0 0; - } -} -/* - MODAL -*/ -#redactor-modal-overlay { - position: fixed; - top: 0; - left: 0; - margin: auto; - overflow: auto; - width: 100%; - height: 100%; - background-color: #000 !important; - filter: alpha(opacity=30); - -moz-opacity: 0.3; - opacity: 0.3; -} -#redactor-modal-box { + background-position: 0 0; } } + +.redactor-box-fullscreen { + z-index: 1051; position: fixed; top: 0; left: 0; - bottom: 0; - right: 0; - overflow-x: hidden; - overflow-y: auto; -} -#redactor-modal { - outline: 0; - position: relative; - margin: auto; - margin-bottom: 20px; - padding: 0; - background: #fff; - color: #000; - font-size: 14px !important; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - box-shadow: 0 1px 70px rgba(0, 0, 0, 0.5); -} -#redactor-modal header { - padding: 30px 40px 5px 40px; - font-size: 18px; - font-weight: bold; -} -#redactor-modal section { - padding: 30px 40px 50px 40px; -} -#redactor-modal label { - display: block; - float: none !important; - margin: 15px 0 3px 0 !important; - padding: 0; -} -#redactor-modal input[type="radio"], -#redactor-modal input[type="checkbox"] { - position: relative; - top: -1px; -} -#redactor-modal select { - width: 100%; -} -#redactor-modal input[type="text"], -#redactor-modal input[type="password"], -#redactor-modal input[type="email"], -#redactor-modal input[type="url"], -#redactor-modal textarea { - position: relative; - z-index: 2; - margin: 0; - padding: 5px 4px; - height: 28px; - border: 1px solid #ccc; - border-radius: 1px; - background-color: white; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset; - color: #333; - width: 100%; - font-size: 14px; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - -moz-transition: border 0.3s ease-in; - transition: border 0.3s ease-in; -} -#redactor-modal input[type="text"]:focus, -#redactor-modal input[type="password"]:focus, -#redactor-modal input[type="email"]:focus, -#redactor-modal input[type="url"]:focus, -#redactor-modal textarea:focus { - outline: none; - border-color: #5ca9e4; - box-shadow: 0 0 0 2px rgba(70, 161, 231, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; -} -#redactor-modal input[type="text"].redactor-input-error, -#redactor-modal input[type="password"].redactor-input-error, -#redactor-modal input[type="email"].redactor-input-error, -#redactor-modal input[type="url"].redactor-input-error, -#redactor-modal textarea.redactor-input-error { - border-color: #e82f2f; - box-shadow: 0 0 0 2px rgba(232, 47, 47, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2) inset; -} -#redactor-modal textarea { - display: block; - margin-top: 4px; - line-height: 1.4em; -} -/* - Tabs in Modal -*/ -#redactor-modal-tabber { - margin-bottom: 15px; - font-size: 12px; -} -#redactor-modal-tabber a { - border: 1px solid #ddd; - line-height: 1; - padding: 8px 15px; - margin-right: -1px; - text-decoration: none; - color: #000; -} -#redactor-modal-tabber a:hover { - background-color: #1f78d8; - border-color: #1f78d8; - color: #fff; -} -#redactor-modal-tabber a.active { - cursor: default; - background-color: #ddd; - border-color: #ddd; - color: rgba(0, 0, 0, 0.6); -} -/* - List in Modal -*/ -#redactor-modal #redactor-modal-list { - margin-left: 0; - padding-left: 0; - list-style: none; - max-height: 250px; - overflow-x: auto; -} -#redactor-modal #redactor-modal-list li { - border-bottom: 1px solid #ddd; -} -#redactor-modal #redactor-modal-list li:last-child { - border-bottom: none; -} -#redactor-modal #redactor-modal-list a { - padding: 10px 5px; - color: #000; - text-decoration: none; - font-size: 13px; - display: block; - position: relative; -} -#redactor-modal #redactor-modal-list a:hover { - background-color: #eee; -} -#redactor-modal-close { - position: absolute; - top: 10px; - right: 10px; - width: 30px; - height: 30px; - text-align: right; - color: #bbb; - font-size: 30px; - font-weight: 300; - cursor: pointer; - -webkit-appearance: none; - padding: 0; - border: 0; - background: 0; - outline: none; -} -#redactor-modal-close:hover { - color: #000; -} -#redactor-modal footer button { - position: relative; - width: 100%; - padding: 14px 16px; - margin: 0; - outline: none; - border: none; - background-color: #ddd; - color: #000; - text-align: center; - text-decoration: none; - font-weight: normal; - font-size: 12px; - font-family: Arial, Helvetica, Verdana, Tahoma, sans-serif !important; - line-height: 1; - cursor: pointer; -} -#redactor-modal footer button:hover { - color: #777; - background: none; - background: #bbb; - text-decoration: none; -} -#redactor-modal footer button.redactor-modal-delete-btn { - background: none; - color: #fff; - background-color: #b52525; -} -#redactor-modal footer button.redactor-modal-delete-btn:hover { - color: rgba(255, 255, 255, 0.6); - background-color: #881b1b; -} -#redactor-modal footer button.redactor-modal-action-btn { - background: none; - color: #fff; - background-color: #2461b5; -} -#redactor-modal footer button.redactor-modal-action-btn:hover { - color: rgba(255, 255, 255, 0.6); - background-color: #1a4580; -} -/* - ############################################## + width: 100%; } - DROPDOWN FORMATTING +.redactor-box-fullscreen-target { + position: absolute !important; } - ############################################## -*/ -.redactor-dropdown .redactor-formatting-blockquote { - color: rgba(0, 0, 0, 0.4); - font-style: italic; -} -.redactor-dropdown .redactor-formatting-pre { - font-family: monospace, sans-serif; -} -.redactor-dropdown .redactor-formatting-h1 { - font-size: 36px; - line-height: 36px; - font-weight: bold; -} -.redactor-dropdown .redactor-formatting-h2 { - font-size: 24px; - line-height: 36px; - font-weight: bold; -} -.redactor-dropdown .redactor-formatting-h3 { - font-size: 21px; - line-height: 30px; - font-weight: bold; -} -.redactor-dropdown .redactor-formatting-h4 { - font-size: 18px; - line-height: 26px; - font-weight: bold; -} -.redactor-dropdown .redactor-formatting-h5 { - font-size: 16px; - line-height: 23px; - font-weight: bold; -} -/* - ############################################## +.redactor-body-fullscreen .redactor-dropdown, +.redactor-body-fullscreen .redactor-context-toolbar { + z-index: 1052; } - CONTENT STYLES +.redactor-body-fullscreen #redactor-overlay { + z-index: 1098; } - ############################################## -*/ -.redactor-editor code, -.redactor-editor pre { - font-family: Menlo, Monaco, monospace, sans-serif !important; - cursor: text; -} -.redactor-editor div, -.redactor-editor p, -.redactor-editor ul, -.redactor-editor ol, -.redactor-editor table, -.redactor-editor dl, -.redactor-editor blockquote, -.redactor-editor pre { - font-size: 14px; - line-height: 1.6em; -} -.redactor-editor a { - color: #15c; - text-decoration: underline; -} -.redactor-editor object, -.redactor-editor embed, -.redactor-editor video, -.redactor-editor img { - max-width: 100%; - width: auto; -} -.redactor-editor video, -.redactor-editor img { - height: auto; -} -.redactor-editor div, -.redactor-editor p, -.redactor-editor ul, -.redactor-editor ol, -.redactor-editor table, -.redactor-editor dl, -.redactor-editor figure, -.redactor-editor blockquote, -.redactor-editor pre { - margin: 0; - margin-bottom: 15px; - border: none; - background: none; - box-shadow: none; -} -.redactor-editor iframe, -.redactor-editor object, -.redactor-editor hr { - margin-bottom: 15px; -} -.redactor-editor blockquote { - margin-left: 1.6em !important; - padding: 0; - text-align: left; - color: #777; - font-style: italic; -} -.redactor-editor blockquote:before, -.redactor-editor blockquote:after { - content: ''; -} -.redactor-editor ul, -.redactor-editor ol { - padding-left: 2em; -} -.redactor-editor ul ul, -.redactor-editor ol ol, -.redactor-editor ul ol, -.redactor-editor ol ul { - margin: 2px; - padding: 0; - padding-left: 2em; - border: none; -} -.redactor-editor ol ol li { - list-style-type: lower-alpha; -} -.redactor-editor ol ol ol li { - list-style-type: lower-roman; -} -.redactor-editor dl dt { - font-weight: bold; -} -.redactor-editor dd { - margin-left: 1em; -} -.redactor-editor table { - border-collapse: collapse; - font-size: 1em; - width: 100%; -} -.redactor-editor table td, -.redactor-editor table th { - padding: 5px; - border: 1px solid #ddd; - vertical-align: top; -} -.redactor-editor table thead td, -.redactor-editor table th { - font-weight: bold; - border-bottom-color: #888; -} -.redactor-editor code { - background-color: #d8d7d7; -} -.redactor-editor pre { - padding: 1em; - border: 1px solid #ddd; - border-radius: 3px; - background: #f8f8f8; - font-size: 90%; -} -.redactor-editor hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; -} -.redactor-editor h1, -.redactor-editor h2, -.redactor-editor h3, -.redactor-editor h4, -.redactor-editor h5, -.redactor-editor h6 { - font-weight: bold; - color: #000; - padding: 0; - background: none; - text-rendering: optimizeLegibility; - margin: 0 0 .5em 0; -} -.redactor-editor h1, -.redactor-editor h2, -.redactor-editor h3, -.redactor-editor h4 { - line-height: 1.3; -} -.redactor-editor h1 { - font-size: 36px; -} -.redactor-editor h2 { - font-size: 24px; - margin-bottom: .7em; -} -.redactor-editor h3 { - font-size: 21px; -} -.redactor-editor h4 { - font-size: 18px; -} -.redactor-editor h5 { - font-size: 16px; -} -.redactor-editor h6 { - font-size: 12px; - text-transform: uppercase; -} +.redactor-body-fullscreen #redactor-modal { + z-index: 1099; } diff --git a/app/assets/stylesheets/comfy/vendor/redactor/clips.css b/app/assets/stylesheets/comfy/vendor/redactor/clips.css new file mode 100644 index 000000000..8bb71801a --- /dev/null +++ b/app/assets/stylesheets/comfy/vendor/redactor/clips.css @@ -0,0 +1,20 @@ +.redactor-clips-list { + list-style: none; + margin: 0; + padding: 0; +} +.redactor-clips-list li { + display: inline-block; + margin-right: 4px; + margin-bottom: 4px; +} +.redactor-clips-list span { + white-space: nowrap; + background: rgba(0,125,255, .75); + color: #fff; + display: inline-block; + padding: 3px 6px; + line-height: 1; + border-radius: 4px; + cursor: pointer; +} \ No newline at end of file diff --git a/app/assets/stylesheets/comfy/vendor/redactor/filemanager.css b/app/assets/stylesheets/comfy/vendor/redactor/filemanager.css new file mode 100644 index 000000000..94a27d34d --- /dev/null +++ b/app/assets/stylesheets/comfy/vendor/redactor/filemanager.css @@ -0,0 +1,38 @@ +#redactor-filemanager-list { + list-style: none; + margin: 0; + padding: 0; +} +#redactor-filemanager-list li { + position: relative; + line-height: 23px; + border-bottom: 1px solid rgba(0, 0, 0, .05); +} +#redactor-filemanager-list li:last-child { + border-bottom-color: transparent; +} +#redactor-filemanager-list a { + text-decoration: none; + display: block; + color: #000; + padding: 3px 0 2px 0; + font-size: 15px; +} +#redactor-filemanager-list a:hover { + background: #f8f8f8; +} +#redactor-filemanager-list span { + font-family: monospace; + font-size: 11px; + color: #888; +} +#redactor-filemanager-list span.r-file-name { + margin-left: 8px; + top: -2px; + position: relative; +} +#redactor-filemanager-list span.r-file-size { + position: absolute; + right: 10px; + top: 6px; +} diff --git a/app/assets/stylesheets/comfy/vendor/redactor/handle.css b/app/assets/stylesheets/comfy/vendor/redactor/handle.css new file mode 100644 index 000000000..3f28b4006 --- /dev/null +++ b/app/assets/stylesheets/comfy/vendor/redactor/handle.css @@ -0,0 +1,27 @@ +#redactor-handle-list { + font-family: "Trebuchet MS", 'Helvetica Neue', Helvetica, Tahoma, sans-serif; + display: none; + position: absolute; + z-index: 1053; + background-color: #fff; + box-shadow: 0 4px 14px rgba(0, 0, 0, .2); + border-radius: 2px; + width: 264px; + max-height: 250px; + margin: 0; + overflow: auto; + font-size: 14px; + padding: 0; +} +#redactor-handle-list a { + display: block; + text-decoration: none; + padding: 10px 8px; + white-space: nowrap; + color: #000; + border-bottom: 1px solid rgba(0, 0, 0, .05); +} +#redactor-handle-list a:hover { + color: #fff !important; + background-color: #449aef !important; +} \ No newline at end of file diff --git a/app/assets/stylesheets/comfy/vendor/redactor/inlinestyle.css b/app/assets/stylesheets/comfy/vendor/redactor/inlinestyle.css new file mode 100644 index 000000000..9840cabee --- /dev/null +++ b/app/assets/stylesheets/comfy/vendor/redactor/inlinestyle.css @@ -0,0 +1,34 @@ +.redactor-dropdown-inline .redactor-dropdown-item-marked span { + font-size: 14px; + background-color: #fdb833; + color: #000; + text-decoration: none; +} +.redactor-dropdown-inline .redactor-dropdown-item-code span { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + font-size: 13px; + background: rgba(0, 0, 0, .05); +} +.redactor-dropdown-inline .redactor-dropdown-item-variable span { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + font-size: 13px; + color: rgba(0, 0, 0, .5); +} +.redactor-dropdown-inline .redactor-dropdown-item-shortcut span { + font-family: Consolas, Menlo, Monaco, "Courier New", monospace; + font-size: 12px; + padding: 0.25em; + white-space: nowrap; + border: 1px solid #e5e7e9; +} +.redactor-dropdown-inline .redactor-dropdown-item-sup span, +.redactor-dropdown-inline .redactor-dropdown-item-sub span { + position: relative; + font-size: 12px; +} +.redactor-dropdown-inline .redactor-dropdown-item-sup span { + top: -3px; +} +.redactor-dropdown-inline .redactor-dropdown-item-sub span { + top: 3px; +} diff --git a/app/assets/stylesheets/comfy/vendor/redactor/variable.css b/app/assets/stylesheets/comfy/vendor/redactor/variable.css new file mode 100644 index 000000000..96da71f86 --- /dev/null +++ b/app/assets/stylesheets/comfy/vendor/redactor/variable.css @@ -0,0 +1,23 @@ +.redactor-variables-list { + list-style: none; + margin: 0; + padding: 0; +} +.redactor-variables-list li { + display: inline-block; + margin-right: 4px; + margin-bottom: 4px; +} +.redactor-variables-list span { + white-space: nowrap; + background: rgba(0,125,255, .75); + color: #fff; + display: inline-block; + padding: 3px 6px; + line-height: 1; + border-radius: 4px; + cursor: pointer; +} +.redactor-variables-list span.redactor-variables-item-selected { + background: #ee698a; +} \ No newline at end of file From 8e24ebf2ce1f281e1e6dd8984f218a90a6f2ff08 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Thu, 4 Jul 2019 21:50:32 +0200 Subject: [PATCH 03/14] link logo image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22e6a50b8..7bbdd06fa 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Thanks to [Roman Almeida](https://github.com/nasmorn) for contributing OEM Licen Thanks to [Bichinger Software & Consulting](https://www.bichinger.de) for contributing OEM License upgrade to [Redactor 3](http://imperavi.com/redactor) -![Bichinger Software & Consulting](doc/bichinger_logo.png) +[![Bichinger Software & Consulting](doc/bichinger_logo.png)](https://www.bichinger.de) --- From dec72a03e9a6a9576872d9e378456fed55b0a35c Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Thu, 4 Jul 2019 22:17:22 +0200 Subject: [PATCH 04/14] update to redactor 3.2.0 --- .../javascripts/comfy/vendor/redactor.js | 388 ++++++------------ .../stylesheets/comfy/vendor/redactor.css | 2 +- 2 files changed, 124 insertions(+), 266 deletions(-) diff --git a/app/assets/javascripts/comfy/vendor/redactor.js b/app/assets/javascripts/comfy/vendor/redactor.js index 4cf57078e..ee957db1d 100644 --- a/app/assets/javascripts/comfy/vendor/redactor.js +++ b/app/assets/javascripts/comfy/vendor/redactor.js @@ -1,7 +1,7 @@ /* Redactor - Version 3.1.8 - Updated: April 3, 2019 + Version 3.2.0 + Updated: June 21, 2019 http://imperavi.com/redactor/ @@ -1336,7 +1336,7 @@ var $R = function(selector, options) // Globals $R.app = []; -$R.version = '3.1.8'; +$R.version = '3.2.0'; $R.options = {}; $R.modules = {}; $R.services = {}; @@ -4315,8 +4315,8 @@ $R.add('service', 'selection', { return ( selected === text || text.search(selected) !== -1 - || selected.search(new RegExp('^' + text)) !== -1 - || selected.search(new RegExp(text + '$')) !== -1 + || selected.search(new RegExp('^' + this.utils.escapeRegExp(text))) !== -1 + || selected.search(new RegExp(this.utils.escapeRegExp(text) + '$')) !== -1 ); }, _isInlineNode: function(node) @@ -4741,7 +4741,9 @@ $R.add('service', 'toolbar', { if (tags.indexOf(key) !== -1) { button = this.getButton(buttons[key]); - button.setActive(); + if (button) { + button.setActive(); + } } } @@ -4793,12 +4795,6 @@ $R.add('service', 'toolbar', { return ($btn.length !== 0) ? $btn.dataget('data-button-instance') : false; }, - getButtonByIndex: function(index) - { - var $btn = this.$toolbar.find('.re-button').eq(index); - - return ($btn.length !== 0) ? $btn.dataget('data-button-instance') : false; - }, getButtons: function() { var buttons = []; @@ -4827,29 +4823,12 @@ $R.add('service', 'toolbar', { { position = position || 'end'; - var index = this._getButtonIndex(name); var $button = $R.create('toolbar.button', this.app, name, btnObj); - if (btnObj.observe) { this.opts.activeButtonsObservers[name] = { observe: btnObj.observe, button: $button }; } - // api added - if (start !== true) - { - if (index === 0) position = 'first'; - else if (index !== -1) - { - var $elm = this.getButtonByIndex(index-1); - if ($elm) - { - position = 'after'; - $el = $elm; - } - } - } - if (this.is()) { if (position === 'first') this.$toolbar.prepend($button); @@ -4927,12 +4906,6 @@ $R.add('service', 'toolbar', { }, // private - _getButtonIndex: function(name) - { - var index = this.buttons.indexOf(name); - - return (index === -1) ? false : index; - }, _findButton: function(selector) { return (this.is()) ? this.$toolbar.find(selector) : $R.dom(); @@ -5250,7 +5223,7 @@ $R.add('class', 'toolbar.dropdown', { // local this.name = name; this.started = false; - this.items = items; + this.items = (name === 'format') ? $R.extend({}, true, items) : items; this.$items = []; }, // public @@ -5596,8 +5569,8 @@ $R.add('service', 'cleaner', { html = this._setSpanAttr(html); html = this._setStyleCache(html); html = this.removeTags(html, this.deniedTags); - //html = (this.opts.removeScript) ? this._removeScriptTag(html) : this._replaceScriptTag(html); - html = (this.opts.removeScript) ? this._removeScriptTag(html) : html; + html = (this.opts.removeScript) ? this._removeScriptTag(html) : this._replaceScriptTag(html); + //html = (this.opts.removeScript) ? this._removeScriptTag(html) : html; html = (this.opts.removeComments) ? this.removeComments(html) : html; html = (this._isSpacedEmpty(html)) ? this.opts.emptyHtml : html; @@ -5607,6 +5580,23 @@ $R.add('service', 'cleaner', { // clear wrapped components html = this._cleanWrapped(html); + // remove image attributes + var $wrapper = this.utils.buildWrapper(html); + var imageattrs = ['alt', 'title', 'src', 'class', 'width', 'height', 'srcset']; + $wrapper.find('img').each(function(node) { + if (node.attributes.length > 0) { + var attrs = node.attributes; + for (var i = attrs.length - 1; i >= 0; i--) { + if (imageattrs.indexOf(attrs[i].name) === -1) { + node.removeAttribute(attrs[i].name); + } + } + } + }); + + // get wrapper html + html = this.utils.getWrapperHtml($wrapper); + // paragraphize html = (paragraphize) ? this.paragraphize(html) : html; @@ -5616,6 +5606,8 @@ $R.add('service', 'cleaner', { { html = this.removeInvisibleSpaces(html); + html = html.replace(/$/g, '$'); + // empty if (this._isSpacedEmpty(html)) return ''; if (this._isParagraphEmpty(html)) return ''; @@ -5740,6 +5732,19 @@ $R.add('service', 'cleaner', { node.setAttribute('style', style); }); + // remove image attributes + var imageattrs = ['alt', 'title', 'src', 'class', 'width', 'height', 'srcset']; + $wrapper.find('img').each(function(node) { + if (node.attributes.length > 0) { + var attrs = node.attributes; + for (var i = attrs.length - 1; i >= 0; i--) { + if (imageattrs.indexOf(attrs[i].name) === -1) { + node.removeAttribute(attrs[i].name); + } + } + } + }); + // remove empty span $wrapper.find('span').each(function(node) { @@ -6171,75 +6176,45 @@ $R.add('service', 'cleaner', { _parseWordLists: function($wrapper) { var lastLevel = 0; - var pnt = null; var $item = null; var setPnt = false; + var $list = null; - $wrapper.find('p').each(function(node) - { + $wrapper.find('p').each(function(node) { var $node = $R.dom(node); var currentLevel = $node.data('_listLevel'); - if (currentLevel !== null) - { + if (currentLevel !== null) { var txt = $node.text(); - var listTag = '
    '; - if (/^\s*\w+\./.test(txt)) - { - var matches = /([0-9])\./.exec(txt); - if (matches) - { - var start = parseInt(matches[1], 10); - listTag = (start > 1) ? '
      ' : '
        '; - } - else - { - listTag = '
          '; - } - } + var listTag = (/^\s*\w+\./.test(txt)) ? '
            ' : '
              '; - if (currentLevel > lastLevel) - { - if (lastLevel === 0) - { - $node.before(listTag); - pnt = $node.prev(); - } - else - { - var $list = $R.dom(listTag); - - if ($item) - { - $item.append($list); - pnt = $list; - setPnt = true; - - } - else - { - pnt.append($list); - } + if ($node.hasClass('MsoListParagraphCxSpFirst')) { + $node.before(listTag); + $list = $node.prev(); + } + if (currentLevel > lastLevel && lastLevel !== 0) { + var $list2 = $R.dom(listTag); + if ($item) { + $item.append($list2); + $list = $list2; + setPnt = true; } } $node.find('span').first().unwrap(); $item = $R.dom('
            • ' + $node.html().trim() + '
            • '); - pnt.append($item); + $list.append($item); $node.remove(); - if (setPnt) - { - pnt = pnt.parent(); + if (setPnt) { + $list = $list.parent(); } lastLevel = currentLevel; setPnt = false; - } - else - { + else { lastLevel = 0; } }); @@ -6296,11 +6271,11 @@ $R.add('service', 'cleaner', { // replace _replaceScriptTag: function(html) { - return html.replace(this.reScriptTag, '
              $2
              '); + return html.replace(this.reScriptTag, ''); }, _unreplaceScriptTag: function(html) { - return html.replace(/
              ]?)>([\w\W]*?)<\/pre>/gi, '$2');
              +        return html.replace(/');
                   },
               	_replaceNlToBr: function(html)
               	{
              @@ -6471,7 +6446,6 @@ $R.add('class', 'cleaner.figure', {
               
                       if ($figure.length === 0)
                       {
              -
                           var $parent = ($link.length !== 0) ? $link.closest('p') : $node.closest('p');
                           if (this.opts.imageFigure === false && $parent.length !== 0)
                           {
              @@ -6481,6 +6455,10 @@ $R.add('class', 'cleaner.figure', {
                           }
                           else
                           {
              +                if ($parent.length !== 0) {
              +                    $parent.unwrap();
              +                }
              +
                               $figure = ($link.length !== 0) ? $link.wrap('
              ') : $node.wrap('
              '); } } @@ -10047,7 +10025,7 @@ $R.add('service', 'utils', { // escape escapeRegExp: function(s) { - return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + return s.replace(/[-\/\\^$*~+?.()|[\]{}]/g, '\\$&'); }, // random @@ -10766,14 +10744,14 @@ $R.add('class', 'editor.events', { contextmenu: function(e) { // chrome crashes fix - this.editor.disableNonEditables(); + //this.editor.disableNonEditables(); - setTimeout(function() - { - this.editor.enableNonEditables(); - this.app.broadcast('contextmenu', e); + //setTimeout(function() + //{ + // this.editor.enableNonEditables(); + // this.app.broadcast('contextmenu', e); - }.bind(this), 0); + //}.bind(this), 0); }, click: function(e) { @@ -10788,7 +10766,7 @@ $R.add('class', 'editor.events', { this.selection.setRange(range) } - // observe bottom click + // observe bottom click & start click var $target = $R.dom(e.target); if ($target.hasClass('redactor-in')) { @@ -11060,13 +11038,6 @@ $R.add('module', 'source', { if (this.app.isStarted()) html = this.app.broadcast('source.open', html); - // insert markers - var sourceSelection = $R.create('source.selection', this.app); - - var editorHtml = sourceSelection.insertMarkersToEditor(); - editorHtml = this.cleaner.output(editorHtml, false); - editorHtml = editorHtml.trim(); - // get height var editorHeight = $editor.height(); @@ -11079,10 +11050,13 @@ $R.add('module', 'source', { $source.on('keydown.redactor-source-events', this._onTabKey.bind(this)); $source.on('focus.redactor-source-events', this._onFocus.bind(this)); - $container.addClass('redactor-source-view'); - - // offset markers - sourceSelection.setSelectionOffsetSource(editorHtml); + if (this.opts.source.hasOwnProperty('codemirror')) { + var opts = (typeof this.opts.source.codemirror === 'object') ? this.opts.source.codemirror : {}; + this.codemirror = CodeMirror.fromTextArea($source.get(), opts); + } + else { + $container.addClass('redactor-source-view'); + } // buttons setTimeout(function() @@ -11103,10 +11077,11 @@ $R.add('module', 'source', { var $container = this.container.getElement(); var html = $source.val(); + if (this.opts.source.hasOwnProperty('codemirror')) { + html = this.codemirror.getValue(); + this.codemirror.toTextArea(); + } - // insert markers - var sourceSelection = $R.create('source.selection', this.app); - html = sourceSelection.insertMarkersToSource(html); // clean html = this.cleaner.input(html, true); @@ -11127,9 +11102,7 @@ $R.add('module', 'source', { setTimeout(function() { - if (sourceSelection.isOffset()) this.selection.restoreMarkers(); - else if (sourceSelection.isOffsetEnd()) this.editor.endFocus(); - else this.editor.startFocus(); + this.editor.startFocus(); // widget's scripts this.component.executeScripts(); @@ -11243,148 +11216,6 @@ $R.add('module', 'source', { $btn.setInactive(); } }); -$R.add('class', 'source.selection', { - init: function(app) - { - this.app = app; - this.utils = app.utils; - this.source = app.source; - this.editor = app.editor; - this.marker = app.marker; - this.component = app.component; - this.selection = app.selection; - - // local - this.markersOffset = false; - this.markersOffsetEnd = false; - }, - insertMarkersToEditor: function() - { - var $editor = this.editor.getElement(); - var start = this.marker.build('start'); - var end = this.marker.build('end'); - var component = this.component.getActive(); - if (component) - { - this.marker.remove(); - var $component = $R.dom(component); - - $component.after(end); - $component.after(start); - } - else if (window.getSelection && this.selection.is()) - { - this.marker.insert('both'); - } - - return this._getHtmlAndRemoveMarkers($editor); - }, - setSelectionOffsetSource: function(editorHtml) - { - var start = 0; - var end = 0; - var $source = this.source.getElement(); - if (editorHtml !== '') - { - var startMarker = this.utils.removeInvisibleChars(this.marker.buildHtml('start')); - var endMarker = this.utils.removeInvisibleChars(this.marker.buildHtml('end')); - - start = this._strpos(editorHtml, startMarker); - end = this._strpos(editorHtml, endMarker) - endMarker.toString().length - 2; - - if (start === false) - { - start = 0; - end = 0; - } - } - - $source.get().setSelectionRange(start, end); - $source.get().scrollTop = 0; - - setTimeout(function() { $source.focus(); }.bind(this), 0); - }, - isOffset: function() - { - return this.markersOffset; - }, - isOffsetEnd: function() - { - return this.markersOffsetEnd; - }, - insertMarkersToSource: function(html) - { - var $source = this.source.getElement(); - var markerStart = this.marker.buildHtml('start'); - var markerEnd = this.marker.buildHtml('end'); - - var markerLength = markerStart.toString().length; - var startOffset = this._enlargeOffset(html, $source.get().selectionStart); - var endOffset = this._enlargeOffset(html, $source.get().selectionEnd); - var sizeOffset = html.length; - - if (startOffset === sizeOffset) - { - this.markersOffsetEnd = true; - } - else if (startOffset !== 0 && endOffset !== 0) - { - this.markersOffset = true; - - html = html.substr(0, startOffset) + markerStart + html.substr(startOffset); - html = html.substr(0, endOffset + markerLength) + markerEnd + html.substr(endOffset + markerLength); - } - else - { - this.markersOffset = false; - } - - return html; - }, - - // private - _getHtmlAndRemoveMarkers: function($editor) - { - var html = $editor.html(); - $editor.find('.redactor-selection-marker').remove(); - - return html; - }, - _strpos: function(haystack, needle, offset) - { - var i = haystack.indexOf(needle, offset); - return i >= 0 ? i : false; - }, - _enlargeOffset: function(html, offset) - { - var htmlLength = html.length; - var c = 0; - - if (html[offset] === '>') - { - c++; - } - else - { - for(var i = offset; i <= htmlLength; i++) - { - c++; - - if (html[i] === '>') - { - break; - } - else if (html[i] === '<' || i === htmlLength) - { - c = 0; - break; - } - } - } - - return offset + c; - } -}); $R.add('module', 'observer', { init: function(app) { @@ -14937,6 +14768,11 @@ $R.add('class', 'input.enter', { var block = this.selection.getBlock(); var data = this.inspector.parse(current); var blockTag = (block) ? block.tagName.toLowerCase() : false; + var $variable = $R.dom(current).closest('[data-redactor-type=variable]'); + // variable parent + if ($variable.length !== 0) { + this.caret.setAfter($variable); + } // pre if (data.isPre()) @@ -15210,6 +15046,7 @@ $R.add('class', 'input.paste', { // clean html = html.trim(); html = (this.isRawCode) ? html : this.cleaner.paste(html); + html = html.trim(); html = (this.isRawCode) ? this.cleaner.encodePhpCode(html) : html; // paste callback @@ -15221,17 +15058,27 @@ $R.add('class', 'input.paste', { // stop input if (!this.opts.input) return; - // autoparse - if (this.opts.autoparse && this.opts.autoparsePaste) - { - html = this.autoparser.parse(html); + var nodes = []; + if (this.isRawCode) { + var textNode = document.createTextNode(html) + nodes = this.insertion.insertNode(textNode, 'after'); + + this.app.broadcast('pasted', nodes); } + else { - var nodes = (this.dropPasted) ? this.insertion.insertToPoint(e, html, this.pointInserted) : this.insertion.insertHtml(html); + // autoparse + if (this.opts.autoparse && this.opts.autoparsePaste) + { + html = this.autoparser.parse(html); + } - // pasted callback - this.app.broadcast('pasted', nodes); - this.app.broadcast('autoparseobserve'); + nodes = (this.dropPasted) ? this.insertion.insertToPoint(e, html, this.pointInserted) : this.insertion.insertHtml(html); + + // pasted callback + this.app.broadcast('pasted', nodes); + this.app.broadcast('autoparseobserve'); + } }, _insertFiles: function(e, files) { @@ -16626,6 +16473,7 @@ $R.add('class', 'image.component', { var imageMargin = ''; var textAlign = ''; var $el = this; + var $img = this.find('img'); var $figcaption = this.find('figcaption'); if (typeof this.opts.imagePosition === 'object') @@ -16644,6 +16492,8 @@ $R.add('class', 'image.component', { } else { + var width = $img.width(); + switch (align) { case 'left': @@ -16659,9 +16509,13 @@ $R.add('class', 'image.component', { break; } - $el.css({ 'float': imageFloat, 'margin': imageMargin, 'text-align': textAlign }); + $el.css({ 'float': imageFloat, maxWidth: width + 'px', 'margin': imageMargin, 'text-align': textAlign }); $el.attr('rel', $el.attr('style')); + if (align === 'none') { + $el.css('max-width', ''); + } + if (align === 'center') { $figcaption.css('text-align', 'center'); @@ -16871,6 +16725,7 @@ $R.add('class', 'image.resize', { x : e.pageX, y : e.pageY, el : this.$resizableImage, + $figure: this.$resizableImage.closest('figure'), ratio: this.$resizableImage.width() / this.$resizableImage.height(), h: this.$resizableImage.height() }; @@ -16913,6 +16768,9 @@ $R.add('class', 'image.resize', { if (height < 20 || width < 100) return; if (this._getResizableBoxWidth() <= width) return; + if (this.resizeHandle.$figure.length !== 0 && this.resizeHandle.$figure.css('max-width') !== '') { + this.resizeHandle.$figure.css('max-width', width + 'px'); + } this.resizeHandle.el.attr({width: width, height: height}); this.resizeHandle.el.width(width); this.resizeHandle.el.height(height); diff --git a/app/assets/stylesheets/comfy/vendor/redactor.css b/app/assets/stylesheets/comfy/vendor/redactor.css index 4b01a8d53..c4a83b22f 100644 --- a/app/assets/stylesheets/comfy/vendor/redactor.css +++ b/app/assets/stylesheets/comfy/vendor/redactor.css @@ -1268,7 +1268,7 @@ position: absolute; font-size: 10px; font-weight: normal; - opacity: .3; + opacity: .5; left: -26px; top: 50%; margin-top: -7px; From 384c11053128c4b6d2b8aaf65be7b2a106ffb960 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Tue, 9 Jul 2019 18:44:43 +0200 Subject: [PATCH 05/14] update deprecated selenium drivers, deactivate strict w3c rule leading to absence of console log access --- Gemfile | 2 +- test/test_helper.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index ebba18473..98350d904 100644 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,7 @@ group :development, :test do gem "kaminari", "~> 1.1.1" gem "puma", "~> 3.11.2" gem "rubocop", "~> 0.55.0", require: false - gem "selenium-webdriver", "~> 3.9.0" + gem "webdrivers", '~> 4.1.0' gem "sqlite3", "~> 1.3.13" end diff --git a/test/test_helper.rb b/test/test_helper.rb index e13a74879..7c46fe980 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -238,7 +238,7 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase Capybara.enable_aria_label = true - driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400] + driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: { w3c: false })} teardown :assert_no_javascript_errors From 8251b76d8de2be11d68bced93a62dfb1cbdbacdd Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Tue, 9 Jul 2019 19:15:44 +0200 Subject: [PATCH 06/14] prevent selenium screenshots from overwriting each other (use unique test names) --- test/system/layouts_frontend_test.rb | 2 +- test/system/pages_frontend_test.rb | 4 ++-- test/system/sites_frontend_test.rb | 2 +- test/system/snippets_frontend_test.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/system/layouts_frontend_test.rb b/test/system/layouts_frontend_test.rb index c16a5ba4a..78a5b285a 100644 --- a/test/system/layouts_frontend_test.rb +++ b/test/system/layouts_frontend_test.rb @@ -8,7 +8,7 @@ class LayoutsFrontendTest < ApplicationSystemTestCase @site = comfy_cms_sites(:default) end - def test_new_identifier + def test_layouts_new_identifier visit_p new_comfy_admin_cms_site_layout_path(@site) fill_in "Layout Name", with: "Test Layout" assert_equal "test-layout", find_field("Identifier").value diff --git a/test/system/pages_frontend_test.rb b/test/system/pages_frontend_test.rb index c4da4068d..ac3700ba6 100644 --- a/test/system/pages_frontend_test.rb +++ b/test/system/pages_frontend_test.rb @@ -8,13 +8,13 @@ class PagesFrontendTest < ApplicationSystemTestCase @site = comfy_cms_sites(:default) end - def test_new_identifier + def test_pages_new_identifier visit_p new_comfy_admin_cms_site_page_path(@site) fill_in "Label", with: "Test Page" assert_equal "test-page", find_field("Slug").value end - def test_change_to_invalid_fragment_and_back + def test_pages_change_to_invalid_fragment_and_back valid_layout = comfy_cms_layouts(:default) valid_layout.update_column(:content, "{{ cms:text content }}") diff --git a/test/system/sites_frontend_test.rb b/test/system/sites_frontend_test.rb index 4131433e6..8172fcd50 100644 --- a/test/system/sites_frontend_test.rb +++ b/test/system/sites_frontend_test.rb @@ -4,7 +4,7 @@ class SitesFrontendTest < ApplicationSystemTestCase - def test_new_identifier + def test_sites_new_identifier visit_p new_comfy_admin_cms_site_path fill_in "Label", with: "Test Site" assert_equal "test-site", find_field("Identifier").value diff --git a/test/system/snippets_frontend_test.rb b/test/system/snippets_frontend_test.rb index ad75c379a..6a11bcffc 100644 --- a/test/system/snippets_frontend_test.rb +++ b/test/system/snippets_frontend_test.rb @@ -8,7 +8,7 @@ class SnippetsFrontendTest < ApplicationSystemTestCase @site = comfy_cms_sites(:default) end - def test_new_identifier + def test_snippets_new_identifier visit_p new_comfy_admin_cms_site_snippet_path(@site) fill_in "Label", with: "Test Snippet" assert_equal "test-snippet", find_field("Identifier").value From 206610f4889b1a2a434133577ae772d4d7a75458 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Tue, 9 Jul 2019 20:07:13 +0200 Subject: [PATCH 07/14] please rubocop --- Gemfile | 2 +- test/test_helper.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 98350d904..62537a70e 100644 --- a/Gemfile +++ b/Gemfile @@ -11,8 +11,8 @@ group :development, :test do gem "kaminari", "~> 1.1.1" gem "puma", "~> 3.11.2" gem "rubocop", "~> 0.55.0", require: false - gem "webdrivers", '~> 4.1.0' gem "sqlite3", "~> 1.3.13" + gem "webdrivers", "~> 4.1.0" end group :development do diff --git a/test/test_helper.rb b/test/test_helper.rb index 7c46fe980..63e9960b8 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -238,7 +238,8 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase Capybara.enable_aria_label = true - driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: { w3c: false })} + caps = Selenium::WebDriver::Remote::Capabilities.chrome(chromeOptions: { w3c: false }) + driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { desired_capabilities: caps } teardown :assert_no_javascript_errors From 574123fe8e0e8b47d53db9450bce7045eeeb3766 Mon Sep 17 00:00:00 2001 From: Kai Machemehl Date: Mon, 5 Aug 2019 10:20:43 +0200 Subject: [PATCH 08/14] First renamed Methods for Redactor, should be more --- app/assets/javascripts/comfy/admin/cms/wysiwyg.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js index 9990a6a4c..5c89cac33 100644 --- a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +++ b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js @@ -50,12 +50,12 @@ if (textareas.length === 0) return; const redactorOptions = buildRedactorOptions(); for (const textarea of textareas) { - redactorInstances.push(new jQuery.Redactor(textarea, redactorOptions)); + redactorInstances.push(new $R(textarea, redactorOptions)); } }, dispose() { for (const redactor of redactorInstances) { - redactor.core.destroy(); + redactor.app.stop(); } redactorInstances.length = 0; } From e48788ef474acaa27dad9befa65b9f523d3e2c96 Mon Sep 17 00:00:00 2001 From: Kai Machemehl Date: Mon, 5 Aug 2019 10:54:56 +0200 Subject: [PATCH 09/14] option naming change for Plugin definedlinks (from definedLinks to definedlinks) --- app/assets/javascripts/comfy/admin/cms/wysiwyg.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js index 5c89cac33..aeba1840a 100644 --- a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +++ b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js @@ -24,8 +24,8 @@ fileManagerJson.searchParams.set('source', 'redactor'); fileManagerJson.searchParams.set('type', 'file'); - const definedLinks = new URL(pagesPath, document.location.href); - definedLinks.searchParams.set('source', 'redactor'); + const definedlinks = new URL(pagesPath, document.location.href); + definedlinks.searchParams.set('source', 'redactor'); return { minHeight: 160, @@ -39,7 +39,7 @@ imageManagerJson, fileUpload, fileManagerJson, - definedLinks + definedlinks }; }; From a9722ed3af8f3c93bada7cecd739220f85d06b34 Mon Sep 17 00:00:00 2001 From: Kai Machemehl Date: Mon, 5 Aug 2019 11:03:09 +0200 Subject: [PATCH 10/14] definedlinks new Object Check is successful for URLS - so converting URL to String --- app/assets/javascripts/comfy/admin/cms/wysiwyg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js index aeba1840a..51b7f1a89 100644 --- a/app/assets/javascripts/comfy/admin/cms/wysiwyg.js +++ b/app/assets/javascripts/comfy/admin/cms/wysiwyg.js @@ -39,7 +39,7 @@ imageManagerJson, fileUpload, fileManagerJson, - definedlinks + definedlinks: definedlinks.toString() }; }; From 86176325d29939165c3cf6e24a0d479aeb05c1ed Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Tue, 6 Aug 2019 16:06:37 +0200 Subject: [PATCH 11/14] modified xhr responses for redactor 3 as per https://github.com/comfy/comfortable-mexican-sofa/wiki/HowTo:-Upgrading-Redactor-Wysiwyg --- app/controllers/comfy/admin/cms/files_controller.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/comfy/admin/cms/files_controller.rb b/app/controllers/comfy/admin/cms/files_controller.rb index 0bac6841e..50bd36bd5 100644 --- a/app/controllers/comfy/admin/cms/files_controller.rb +++ b/app/controllers/comfy/admin/cms/files_controller.rb @@ -24,15 +24,17 @@ def index when "image" file_scope.with_images.collect do |file| { thumb: url_for(file.attachment.variant(combine_options: Comfy::Cms::File::VARIANT_SIZE[:redactor])), - image: url_for(file.attachment), - title: file.label } + url: url_for(file.attachment), + title: file.label, + id: file.id } end else file_scope.collect do |file| { title: file.label, name: file.attachment.filename, - link: url_for(file.attachment), - size: number_to_human_size(file.attachment.byte_size) } + url: url_for(file.attachment), + size: number_to_human_size(file.attachment.byte_size), + id: file.id } end end From 60e89fbf6af844a03a25bb90378d7ce2d0d0d105 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Tue, 6 Aug 2019 19:48:21 +0200 Subject: [PATCH 12/14] handle inexistant root page --- app/models/comfy/cms/page.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/comfy/cms/page.rb b/app/models/comfy/cms/page.rb index 932319410..eb9d2ff1e 100644 --- a/app/models/comfy/cms/page.rb +++ b/app/models/comfy/cms/page.rb @@ -60,7 +60,8 @@ def self.options_for_select(site:, current_page: nil, exclude_self: false) end end - options_for_page.call(site.pages.root) + root_page = site.pages.root + options_for_page.call(root_page) if root_page options end From 1d7aeed79caf397787cfb33c0a6d10e3201e9003 Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Thu, 8 Aug 2019 22:31:36 +0200 Subject: [PATCH 13/14] cleanup --- app/controllers/comfy/admin/cms/files_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/comfy/admin/cms/files_controller.rb b/app/controllers/comfy/admin/cms/files_controller.rb index 50bd36bd5..7c95fdd5c 100644 --- a/app/controllers/comfy/admin/cms/files_controller.rb +++ b/app/controllers/comfy/admin/cms/files_controller.rb @@ -44,7 +44,7 @@ def index files_scope = files_scope .includes(:categories) .for_category(params[:categories]) - .order("comfy_cms_files.position") + .order(:position) @files = comfy_paginate(files_scope, per_page: 50) end end From ab0b210786d478a2c9ad5d61bfb75e9c2ae3a32e Mon Sep 17 00:00:00 2001 From: Niklas Bichinger Date: Thu, 8 Aug 2019 22:32:13 +0200 Subject: [PATCH 14/14] little create refactoring for redactor 3 (multiple file dragging) --- .../comfy/admin/cms/files_controller.rb | 118 ++++++++++++------ 1 file changed, 77 insertions(+), 41 deletions(-) diff --git a/app/controllers/comfy/admin/cms/files_controller.rb b/app/controllers/comfy/admin/cms/files_controller.rb index 7c95fdd5c..91aaf67fc 100644 --- a/app/controllers/comfy/admin/cms/files_controller.rb +++ b/app/controllers/comfy/admin/cms/files_controller.rb @@ -7,7 +7,6 @@ class Comfy::Admin::Cms::FilesController < Comfy::Admin::Cms::BaseController include ActionView::Helpers::NumberHelper - before_action :build_file, only: %i[new create] before_action :load_file, only: %i[edit update destroy] before_action :authorize @@ -16,7 +15,7 @@ def index case params[:source] - # Integration with Redactor 1.0 Wysiwyg + # Integration with Redactor 3 Wysiwyg when "redactor" file_scope = files_scope.limit(100).order(:position) file_hashes = @@ -50,52 +49,21 @@ def index end def new - render + @file = @site.files.new(file_params) end def create - categories_scope = @site.categories.of_type("Comfy::Cms::File") - - if params[:categories] - ids = categories_scope.where(label: params[:categories]).pluck(:id) - @file.category_ids = ids - end - - # Automatically tagging upload if it's done through redactor - if params[:source] == "redactor" - category = categories_scope.find_or_create_by(label: "wysiwyg") - @file.category_ids = [category.id] - end - - @file.save! - case params[:source] - when "plupload" - render partial: "file", object: @file when "redactor" - render json: { - filelink: url_for(@file.attachment), - filename: @file.attachment.filename - } - else - flash[:success] = I18n.t("comfy.admin.cms.files.created") - redirect_to action: :edit, id: @file - end - - rescue ActiveRecord::RecordInvalid - case params[:source] + _create_redactor when "plupload" - render body: @file.errors.full_messages.to_sentence, status: :unprocessable_entity - when "redactor" - render body: nil, status: :unprocessable_entity + _create_plupload else - flash.now[:danger] = I18n.t("comfy.admin.cms.files.creation_failure") - render action: :new + _create end end def edit - render end def update @@ -121,8 +89,70 @@ def destroy protected - def build_file + def _create_redactor + files = @site.files.new(files_params) + # set category ids + categories_scope = @site.categories.of_type("Comfy::Cms::File") + category_ids = [ categories_scope.find_or_create_by(label: "wysiwyg").id ] + if params[:categories].present? + category_ids.concat( categories_scope.where(label: params[:categories]).pluck(:id) ) + end + + files.each do |file| + file.category_ids = category_ids + file.save! + end + + result = + files.each_with_object({}) do |file, hash| + hash["file-#{file.id}"] = { + url: url_for(file.attachment), + id: file.id + } + end + render json: result + rescue ActiveRecord::RecordInvalid + file = + files&.detect do |file| + file.errors.any? + end + render json: {error: true, message: file && file.errors.full_messages.to_sentence}, status: :unprocessable_entity + end + + def _create_plupload + @file = @site.files.new(file_params) + # set category ids + categories_scope = @site.categories.of_type("Comfy::Cms::File") + category_ids = [] + if params[:categories].present? + category_ids.concat( categories_scope.where(label: params[:categories]).pluck(:id) ) + end + @file.category_ids = category_ids + + @file.save! + + render partial: "file", object: @file + rescue ActiveRecord::RecordInvalid + render body: @file.errors.full_messages.to_sentence, status: :unprocessable_entity + end + + def _create @file = @site.files.new(file_params) + # set category ids + categories_scope = @site.categories.of_type("Comfy::Cms::File") + category_ids = [] + if params[:categories].present? + category_ids.concat( categories_scope.where(label: params[:categories]).pluck(:id) ) + end + @file.category_ids = category_ids + + @file.save! + + flash[:success] = I18n.t("comfy.admin.cms.files.created") + redirect_to action: :edit, id: @file + rescue ActiveRecord::RecordInvalid + flash.now[:danger] = I18n.t("comfy.admin.cms.files.creation_failure") + render action: :new end def load_file @@ -134,11 +164,17 @@ def load_file def file_params file = params[:file] + # new() needs a file hash to work, so create an "empty" (at least one attribute must be passed) one if no file + # attributes were passed. unless file.is_a?(Hash) || file.respond_to?(:to_unsafe_hash) - params[:file] = {} - params[:file][:file] = file + params[:file] = {label: nil} end - params.fetch(:file, {}).permit! + params.require(:file).permit(:label, :file, :description, category_ids: []) + end + + def files_params + # convert `file: [, , ...]` to `[{file: }, {file: }, ...]` + params.require(:file).map! { |file| {file: file} } end end