diff --git a/app/assets/javascripts/admin.coffee b/app/assets/javascripts/admin.coffee index de53fc0..7f8ba3a 100644 --- a/app/assets/javascripts/admin.coffee +++ b/app/assets/javascripts/admin.coffee @@ -24,11 +24,34 @@ #= require redactor +#= require redactor_plugins/fontsize +#= require redactor_plugins/fontfamily +#= require redactor_plugins/fontcolor #= require nested_fields -#= require_tree ./note_files - +portlet_to_move = false +content_type_to_move =false +@open_collapse3 = -> + $("#collapse3 .panel").show() + $("#collapse3").show() + +@reset_edit_pane = -> + $("#element_form").html() + $("#collapse3").hide() + +@unselect_portlet = -> + $(".portlet.active").removeClass("active") + reset_edit_pane() + +@cancel_move_portlet = -> + $(".move_message").show() + $(".cancel_message").hide() + + $(".move").removeClass("move") + $(".portlet_placeholder").remove() + portlet_to_move = false + $(document).ready -> $(document).on 'click', 'input.datepicker', -> @@ -61,11 +84,14 @@ $(document).ready -> $(document).on "click", ".portlet", (event) -> - $(".portlet.active").removeClass("active") - $(this).addClass("active") + if !$(this).hasClass("active") and portlet_to_move == false + + $(".portlet.active").removeClass("active") + $(this).addClass("active") - $.ajax({url : $(this).data("edit-link"), type: "GET"}); - $("#collapse3 .trash").attr("href", $(this).data("show-link")); + + $.ajax({url : $(this).data("edit-link"), type: "GET"}); + $("#collapse3 .trash").attr("href", $(this).data("show-link")); event.stopPropagation(); @@ -73,26 +99,43 @@ $(document).ready -> portlet_to_move = false content_type_to_move = false $("#element_form").html("") - $("#collapse3").collapse('hide'); - $("#collapse2").collapse('show'); + $(".portlet.active").removeClass("active") - portlet_to_move = false - content_type_to_move =false + + + + $(document).on "click", "#content_types .content_type", -> - + cancel_move_portlet() + unselect_portlet() + reset_edit_pane() + + type = $(this).data("type") content_type_to_move = type init_portlets_place_holder() $(this).addClass("move") false + + $(document).on "click", ".portlet_handle", -> - id = $(this).data("portlet-id") - portlet_to_move = $("#portlet_"+id) - init_portlets_place_holder() - portlet_to_move.addClass("move") - false + if portlet_to_move == false + + $(".move_message").hide() + $(".cancel_message").show() + + + id = $(this).data("portlet-id") + portlet_to_move = $("#portlet_"+id) + init_portlets_place_holder() + portlet_to_move.addClass("move") + false + else + cancel_move_portlet() + + false $(document).on "click", ".portlet_placeholder", -> if portlet_to_move != false @@ -100,7 +143,7 @@ $(document).ready -> $(".move").removeClass("move") $(".portlet_placeholder").remove() update_block_portlet_order(portlet_to_move.closest(".block_portlets").data("block_id")) - portlet_to_move = false + cancel_move_portlet() else block_id = $(this).closest(".block_portlets").data("block_id") @@ -344,3 +387,14 @@ $(document).on "scroll", () -> $("#toolbar-text, #menu_item_informations").css top : top + +$(document).on "click", "#menu_item_informations .save", -> + $(this).closest(".panel").find("form").submit() + return false + +$(document).on "click", "#menu_item_informations h4", -> + $(this).next(".panel").toggle() + return false + + + diff --git a/app/assets/javascripts/redactor.js b/app/assets/javascripts/redactor.js index 8f6ec66..fe77a60 100755 --- a/app/assets/javascripts/redactor.js +++ b/app/assets/javascripts/redactor.js @@ -1,6 +1,6 @@ /* - Redactor v9.2.4 - Updated: May 15, 2014 + Redactor v10.0.1 + Updated: October 6, 2014 http://imperavi.com/redactor/ @@ -9,28 +9,26 @@ Usage: $('#content').redactor(); */ + (function($) { + 'use strict'; + + if (!Function.prototype.bind) + { + Function.prototype.bind = function(scope) + { + var fn = this; + return function() + { + return fn.apply(scope); + }; + }; + } + var uuid = 0; - "use strict"; - - var Range = function(range) - { - this[0] = range.startOffset; - this[1] = range.endOffset; - - this.range = range; - - return this; - }; - - Range.prototype.equals = function() - { - return this[0] === this[1]; - }; - - var reUrlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig; + var reUrlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig; var reUrlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/; // Plugin @@ -44,19 +42,41 @@ this.each(function() { var instance = $.data(this, 'redactor'); - if (typeof instance !== 'undefined' && $.isFunction(instance[options])) + var func; + + if (options.search(/\./) != '-1') { - var methodVal = instance[options].apply(instance, args); - if (methodVal !== undefined && methodVal !== instance) val.push(methodVal); + 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 return $.error('No such method "' + options + '" for Redactor'); }); } else { this.each(function() { - if (!$.data(this, 'redactor')) $.data(this, 'redactor', Redactor(this, options)); + $.data(this, 'redactor', {}); + $.data(this, 'redactor', Redactor(this, options)); }); } @@ -72,239 +92,238 @@ return new Redactor.prototype.init(el, options); } + // Functionality $.Redactor = Redactor; - $.Redactor.VERSION = '9.2.4'; + $.Redactor.VERSION = '10.0.1'; + $.Redactor.modules = ['core', 'build', 'lang', 'toolbar', 'button', 'dropdown', 'code', + 'clean', 'tidy', 'paragraphize', 'tabifier', 'focus', 'placeholder', 'autosave', 'buffer', 'indent', 'alignment', 'paste', + 'keydown', 'keyup', 'shortcuts', 'line', 'list', 'block', 'inline', 'insert', 'caret', 'selection', 'observe', + 'link', 'image', 'file', 'modal', 'progress', 'upload', 'utils']; + $.Redactor.opts = { - // settings - rangy: false, + // settings + lang: 'en', + direction: 'ltr', // ltr or rtl - iframe: false, - fullpage: false, - css: false, // url + plugins: false, // array - lang: 'en', - direction: 'ltr', // ltr or rtl + focus: false, + focusEnd: false, - placeholder: false, + placeholder: false, - typewriter: false, - wym: false, - mobile: true, - cleanup: true, - tidyHtml: true, - pastePlainText: false, - removeEmptyTags: true, - cleanSpaces: true, - cleanFontTag: true, - templateVars: false, - xhtml: false, + visual: true, + tabindex: false, - visual: true, - focus: false, - tabindex: false, - autoresize: true, - minHeight: false, - maxHeight: false, - shortcuts: { - 'ctrl+m, meta+m': "this.execCommand('removeFormat', false)", - 'ctrl+b, meta+b': "this.execCommand('bold', false)", - 'ctrl+i, meta+i': "this.execCommand('italic', false)", - 'ctrl+h, meta+h': "this.execCommand('superscript', false)", - 'ctrl+l, meta+l': "this.execCommand('subscript', false)", - 'ctrl+k, meta+k': "this.linkShow()", - 'ctrl+shift+7': "this.execCommand('insertorderedlist', false)", - 'ctrl+shift+8': "this.execCommand('insertunorderedlist', false)" - }, - shortcutsAdd: { - 'ctrl+3': "this.execCommand('removeFormat', false)" + minHeight: false, + maxHeight: false, - }, + linebreaks: false, + replaceDivs: true, + paragraphize: true, + cleanStyleOnEnter: false, + enterKey: true, - autosave: false, // false or url - autosaveInterval: 60, // seconds + cleanOnPaste: true, + cleanSpaces: true, + pastePlainText: false, - plugins: false, // array + autosave: false, // false or url + autosaveName: false, + autosaveInterval: 60, // seconds + autosaveOnChange: false, - //linkAnchor: true, - //linkEmail: true, - linkProtocol: 'http://', - linkNofollow: false, - linkSize: 50, - predefinedLinks: false, // json url (ex. /some-url.json ) or false + linkTooltip: true, + linkProtocol: 'http', + linkNofollow: false, + linkSize: 50, - imageFloatMargin: '10px', - imageGetJson: false, // json url (ex. /some-images.json ) or false + imageEditable: true, + imageLink: true, + imagePosition: true, + imageFloatMargin: '10px', + imageResizable: true, - dragUpload: true, // false - imageTabLink: true, - imageUpload: false, // url - imageUploadParam: 'file', // input name - imageResizable: true, + imageUpload: false, + imageUploadParam: 'file', - fileUpload: false, // url - fileUploadParam: 'file', // input name - clipboardUpload: true, // or false - clipboardUploadUrl: false, // url + uploadImageField: false, - dnbImageTypes: ['image/png', 'image/jpeg', 'image/gif'], // or false + dragImageUpload: true, - s3: false, - uploadFields: false, + fileUpload: false, + fileUploadParam: 'file', - observeImages: true, - observeLinks: true, + dragFileUpload: true, - modalOverlay: true, + s3: false, - tabSpaces: false, // true or number of spaces - tabFocus: true, + convertLinks: true, + convertUrlLinks: true, + convertImageLinks: true, + convertVideoLinks: true, - air: false, - airButtons: ['formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', 'outdent', 'indent'], + preSpaces: 4, // or false + tabAsSpaces: false, // true or number of spaces + tabFocus: true, - toolbar: true, - toolbarFixed: false, - toolbarFixedTarget: document, - toolbarFixedTopOffset: 0, // pixels - toolbarFixedBox: false, - toolbarExternal: false, // ID selector - toolbarOverflow: false, - buttonSource: true, + scrollTarget: false, - buttons: ['html', 'formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', - 'outdent', 'indent', 'image', 'video', 'file', 'table', 'link', 'alignment', '|', - 'horizontalrule'], // 'underline', 'alignleft', 'aligncenter', 'alignright', 'justify' - buttonsHideOnMobile: [], + toolbar: true, + toolbarFixed: true, + toolbarFixedTarget: document, + toolbarFixedTopOffset: 0, // pixels + toolbarExternal: false, // ID selector + toolbarOverflow: false, - activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist', - 'alignleft', 'aligncenter', 'alignright', 'justify', 'table'], - activeButtonsStates: { - b: 'bold', - strong: 'bold', - i: 'italic', - em: 'italic', - del: 'deleted', - strike: 'deleted', - ul: 'unorderedlist', - ol: 'orderedlist', - u: 'underline', - tr: 'table', - td: 'table', - table: 'table' - }, + buttonSource: false, + buttons: ['html', 'formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', + 'outdent', 'indent', 'image', 'file', 'link', 'alignment', 'horizontalrule'], // + 'underline' - formattingTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + buttonsHide: [], + buttonsHideOnMobile: [], - linebreaks: false, - paragraphy: true, - convertDivs: true, - convertLinks: true, - convertImageLinks: false, - convertVideoLinks: false, - formattingPre: false, - phpTags: false, + formatting: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], + formattingAdd: false, - allowedTags: false, - deniedTags: ['html', 'head', 'link', 'body', 'meta', 'script', 'style', 'applet'], + tabifier: true, - boldTag: 'strong', - italicTag: 'em', + deniedTags: ['html', 'head', 'link', 'body', 'meta', 'script', 'style', 'applet'], + allowedTags: false, // or array - // private - indentValue: 20, - buffer: [], - rebuffer: [], - textareamode: false, - emptyHtml: '

', - invisibleSpace: '​', - rBlockTest: /^(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)$/i, - alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'TD', - 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', - 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], - ownLine: ['area', 'body', 'head', 'hr', 'i?frame', 'link', 'meta', 'noscript', 'style', 'script', 'table', 'tbody', 'thead', 'tfoot'], - contOwnLine: ['li', 'dt', 'dt', 'h[1-6]', 'option', 'script'], - newLevel: ['blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset', 'map', 'ol', 'p', 'pre', 'select', 'td', 'th', 'tr', 'ul'], - blockLevelElements: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'LI', - 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'PRE', 'ADDRESS', 'SECTION', - 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE', 'TD'], + removeComments: false, + replaceTags: [ + ['strike', 'del'] + ], + 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'], - // 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', - 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' - } + // 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' } + } }; // Functionality @@ -315,10 +334,13 @@ DELETE: 46, DOWN: 40, ENTER: 13, + SPACE: 32, ESC: 27, TAB: 9, CTRL: 17, META: 91, + SHIFT: 16, + ALT: 18, LEFT: 37, LEFT_WIN: 91 }, @@ -326,2994 +348,2923 @@ // Initialization init: function(el, options) { - this.rtePaste = false; - this.$element = this.$source = $(el); + this.$element = $(el); this.uuid = uuid++; - // clonning options - var opts = $.extend(true, {}, $.Redactor.opts); + // if paste event detected = true + this.rtePaste = false; + this.$pasteBox = false; - // current settings - this.opts = $.extend( - {}, - opts, - this.$element.data(), - options - ); + this.loadOptions(options); + this.loadModules(); - this.start = true; - this.dropdowns = []; + // formatting storage + this.formatting = {}; - // get sizes - this.sourceHeight = this.$source.css('height'); - this.sourceWidth = this.$source.css('width'); + // block level tags + $.merge(this.opts.blockLevelElements, this.opts.alignmentTags); + this.reIsBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i'); - // dependency of the editor modes - if (this.opts.fullpage) this.opts.iframe = true; - if (this.opts.linebreaks) this.opts.paragraphy = false; - if (this.opts.paragraphy) this.opts.linebreaks = false; - if (this.opts.toolbarFixedBox) this.opts.toolbarFixed = true; - - // the alias for iframe mode - this.document = document; - this.window = window; - - // selection saved - this.savedSel = false; - - // clean setup - this.cleanlineBefore = new RegExp('^<(/?' + this.opts.ownLine.join('|/?' ) + '|' + this.opts.contOwnLine.join('|') + ')[ >]'); - this.cleanlineAfter = new RegExp('^<(br|/?' + this.opts.ownLine.join('|/?' ) + '|/' + this.opts.contOwnLine.join('|/') + ')[ >]'); - this.cleannewLevel = new RegExp('^]'); - - // block level - this.rTestBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i'); - - // setup formatting permissions - if (this.opts.linebreaks === false) - { - if (this.opts.allowedTags !== false) - { - var arrSearch = ['strong', 'em', 'del']; - var arrAdd = ['b', 'i', 'strike']; - - if ($.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p'); - - for (i in arrSearch) - { - if ($.inArray(arrSearch[i], this.opts.allowedTags) != '-1') this.opts.allowedTags.push(arrAdd[i]); - } - } - - if (this.opts.deniedTags !== false) - { - var pos = $.inArray('p', this.opts.deniedTags); - if (pos !== '-1') this.opts.deniedTags.splice(pos, pos); - } - } - - // ie & opera - if (this.browser('msie') || this.browser('opera')) - { - this.opts.buttons = this.removeFromArrayByValue(this.opts.buttons, 'horizontalrule'); - } + // setup allowed and denied tags + this.tidy.setupAllowed(); // load lang - this.opts.curLang = this.opts.langs[this.opts.lang]; + this.lang.load(); // extend shortcuts $.extend(this.opts.shortcuts, this.opts.shortcutsAdd); - // init placeholder - this.placeholderInit(); - - // Build - this.buildStart(); + // start callback + this.core.setCallback('start'); + // build + this.start = true; + this.build.run(); }, - toolbarInit: function(lang) + + 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; + + // init module + this[module] = this[module](); + + var methods = this.getModuleMethods(this[module]); + var len = methods.length; + + // bind methods + for (var z = 0; z < len; z++) + { + this[module][methods[z]] = this[module][methods[z]].bind(this); + } + }, + + core: function() { return { - html: + getObject: function() { - title: lang.html, - func: 'toggle' + return $.extend({}, this); }, - formatting: + getEditor: function() { - title: lang.formatting, - func: 'show', - dropdown: + return this.$editor; + }, + getBox: function() + { + return this.$box; + }, + getElement: function() + { + return this.$element; + }, + getTextarea: function() + { + return this.$textarea; + }, + getToolbar: function() + { + return (this.$toolbar) ? this.$toolbar : false; + }, + addEvent: function(name) + { + this.core.event = name; + }, + getEvent: function() + { + return this.core.event; + }, + setCallback: function(type, e, data) + { + var callback = this.opts[type + 'Callback']; + if ($.isFunction(callback)) { - p: - { - title: lang.paragraph, - func: 'formatBlocks' - }, - blockquote: - { - title: lang.quote, - func: 'formatQuote', - className: 'redactor_format_blockquote' - }, - pre: - { - title: lang.code, - func: 'formatBlocks', - className: 'redactor_format_pre' - }, - h1: - { - title: lang.header1, - func: 'formatBlocks', - className: 'redactor_format_h1' - }, - h2: - { - title: lang.header2, - func: 'formatBlocks', - className: 'redactor_format_h2' - }, - h3: - { - title: lang.header3, - func: 'formatBlocks', - className: 'redactor_format_h3' - }, - h4: - { - title: lang.header4, - func: 'formatBlocks', - className: 'redactor_format_h4' - }, - h5: - { - title: lang.header5, - func: 'formatBlocks', - className: 'redactor_format_h5' - } - } - }, - bold: - { - title: lang.bold, - exec: 'bold' - }, - italic: - { - title: lang.italic, - exec: 'italic' - }, - deleted: - { - title: lang.deleted, - exec: 'strikethrough' - }, - underline: - { - title: lang.underline, - exec: 'underline' - }, - unorderedlist: - { - title: '• ' + lang.unorderedlist, - exec: 'insertunorderedlist' - }, - orderedlist: - { - title: '1. ' + lang.orderedlist, - exec: 'insertorderedlist' - }, - outdent: - { - title: '< ' + lang.outdent, - func: 'indentingOutdent' - }, - indent: - { - title: '> ' + lang.indent, - func: 'indentingIndent' - }, - image: - { - title: lang.image, - func: 'imageShow' - }, - video: - { - title: lang.video, - func: 'videoShow' - }, - file: - { - title: lang.file, - func: 'fileShow' - }, - table: - { - title: lang.table, - func: 'show', - dropdown: - { - insert_table: - { - title: lang.insert_table, - func: 'tableShow' - }, - separator_drop1: - { - name: 'separator' - }, - insert_row_above: - { - title: lang.insert_row_above, - func: 'tableAddRowAbove' - }, - insert_row_below: - { - title: lang.insert_row_below, - func: 'tableAddRowBelow' - }, - insert_column_left: - { - title: lang.insert_column_left, - func: 'tableAddColumnLeft' - }, - insert_column_right: - { - title: lang.insert_column_right, - func: 'tableAddColumnRight' - }, - separator_drop2: - { - name: 'separator' - }, - add_head: - { - title: lang.add_head, - func: 'tableAddHead' - }, - delete_head: - { - title: lang.delete_head, - func: 'tableDeleteHead' - }, - separator_drop3: - { - name: 'separator' - }, - delete_column: - { - title: lang.delete_column, - func: 'tableDeleteColumn' - }, - delete_row: - { - title: lang.delete_row, - func: 'tableDeleteRow' - }, - delete_table: - { - title: lang.delete_table, - func: 'tableDeleteTable' - } - } - }, - link: { - title: lang.link, - func: 'show', - dropdown: - { - link: - { - title: lang.link_insert, - func: 'linkShow' - }, - unlink: - { - title: lang.unlink, - exec: 'unlink' - } - } - }, - alignment: - { - title: lang.alignment, - func: 'show', - dropdown: - { - alignleft: - { - title: lang.align_left, - func: 'alignmentLeft' - }, - aligncenter: - { - title: lang.align_center, - func: 'alignmentCenter' - }, - alignright: - { - title: lang.align_right, - func: 'alignmentRight' - }, - justify: - { - title: lang.align_justify, - func: 'alignmentJustify' - } - } - }, - alignleft: - { - title: lang.align_left, - func: 'alignmentLeft' - }, - aligncenter: - { - title: lang.align_center, - func: 'alignmentCenter' - }, - alignright: - { - title: lang.align_right, - func: 'alignmentRight' - }, - alignjustify: - { - title: lang.align_justify, - func: 'alignmentJustify' - }, - horizontalrule: - { - exec: 'inserthorizontalrule', - title: lang.horizontalrule - } - - } - }, - - // CALLBACKS - callback: function(type, event, data) - { - var callback = this.opts[ type + 'Callback' ]; - if ($.isFunction(callback)) - { - if (event === false) return callback.call(this, data); - else return callback.call(this, event, data); - } - else return data; - }, - - - // DESTROY - destroy: function() - { - clearInterval(this.autosaveInterval); - - $(window).off('.redactor'); - this.$source.off('redactor-textarea'); - this.$element.off('.redactor').removeData('redactor'); - - var html = this.get(); - - if (this.opts.textareamode) - { - this.$box.after(this.$source); - this.$box.remove(); - this.$source.val(html).show(); - } - else - { - var $elem = this.$editor; - if (this.opts.iframe) $elem = this.$element; - - this.$box.after($elem); - this.$box.remove(); - - $elem.removeClass('redactor_editor').removeClass('redactor_editor_wym').removeAttr('contenteditable').html(html).show(); - } - - if (this.opts.toolbarExternal) - { - $(this.opts.toolbarExternal).html(''); - } - - if (this.opts.air) - { - $('#redactor_air_' + this.uuid).remove(); - } - }, - - // API GET - getObject: function() - { - return $.extend({}, this); - }, - getEditor: function() - { - return this.$editor; - }, - getBox: function() - { - return this.$box; - }, - getIframe: function() - { - return (this.opts.iframe) ? this.$frame : false; - }, - getToolbar: function() - { - return (this.$toolbar) ? this.$toolbar : false; - }, - - // CODE GET & SET - get: function() - { - return this.$source.val(); - }, - getCodeIframe: function() - { - this.$editor.removeAttr('contenteditable').removeAttr('dir'); - var html = this.outerHtml(this.$frame.contents().children()); - this.$editor.attr({ 'contenteditable': true, 'dir': this.opts.direction }); - - return html; - }, - set: function(html, strip, placeholderRemove) - { - html = html.toString(); - html = html.replace(/\$/g, '$'); - - if (this.opts.fullpage) this.setCodeIframe(html); - else this.setEditor(html, strip); - - if (html == '') placeholderRemove = false; - if (placeholderRemove !== false) this.placeholderRemoveFromEditor(); - }, - setEditor: function(html, strip) - { - - if (strip !== false) - { - html = this.cleanSavePreCode(html); - - html = this.cleanStripTags(html); - html = this.cleanConvertProtected(html); - html = this.cleanConvertInlineTags(html, true); - - if (this.opts.linebreaks === false) html = this.cleanConverters(html); - else html = html.replace(/([\w\W]*?)<\/p>/gi, '$2
'); - } - - // $ fix - html = html.replace(/&#36;/g, '$'); - - html = this.cleanEmpty(html); - - this.$editor.html(html); - - // set no editable - this.setNonEditable(); - this.setSpansVerified(); - - this.sync(); - }, - setCodeIframe: function(html) - { - var doc = this.iframePage(); - this.$frame[0].src = "about:blank"; - - html = this.cleanConvertProtected(html); - html = this.cleanConvertInlineTags(html); - html = this.cleanRemoveSpaces(html); - - doc.open(); - doc.write(html); - doc.close(); - - // redefine editor for fullpage mode - if (this.opts.fullpage) - { - this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction }); - } - - // set no editable - this.setNonEditable(); - this.setSpansVerified(); - this.sync(); - - }, - setFullpageOnInit: function(html) - { - this.fullpageDoctype = html.match(/^<\!doctype[^>]*>/i); - if (this.fullpageDoctype && this.fullpageDoctype.length == 1) - { - html = html.replace(/^<\!doctype[^>]*>/i, ''); - } - - html = this.cleanSavePreCode(html, true); - html = this.cleanConverters(html); - html = this.cleanEmpty(html); - - // set code - this.$editor.html(html); - - // set no editable - this.setNonEditable(); - this.setSpansVerified(); - this.sync(); - }, - setFullpageDoctype: function() - { - if (this.fullpageDoctype && this.fullpageDoctype.length == 1) - { - var source = this.fullpageDoctype[0] + '\n' + this.$source.val(); - this.$source.val(source); - } - }, - setSpansVerified: function() - { - var spans = this.$editor.find('span'); - var replacementTag = 'inline'; - - $.each(spans, function() { - var outer = this.outerHTML; - - // Replace opening tag - var regex = new RegExp('<' + this.tagName, 'gi'); - var newTag = outer.replace(regex, '<' + replacementTag); - - // Replace closing tag - regex = new RegExp('/, ''); - return html.replace(/<\/span>/, ''); - }, - setNonEditable: function() - { - this.$editor.find('.noneditable').attr('contenteditable', false); - }, - - // SYNC - sync: function(e) - { - var html = ''; - - this.cleanUnverified(); - - if (this.opts.fullpage) html = this.getCodeIframe(); - else html = this.$editor.html(); - - html = this.syncClean(html); - html = this.cleanRemoveEmptyTags(html); - - // is there a need to synchronize - var source = this.cleanRemoveSpaces(this.$source.val(), false); - var editor = this.cleanRemoveSpaces(html, false); - - if (source == editor) - { - // do not sync - return false; - } - - - // fix second level up ul, ol - html = html.replace(/<\/li><(ul|ol)>([\w\W]*?)<\/(ul|ol)>/gi, '<$1>$2'); - - if ($.trim(html) === '
') html = ''; - - // xhtml - if (this.opts.xhtml) - { - var xhtmlTags = ['br', 'hr', 'img', 'link', 'input', 'meta']; - $.each(xhtmlTags, function(i,s) - { - html = html.replace(new RegExp('<' + s + '(.*?[^\/$]?)>', 'gi'), '<' + s + '$1 />'); - }); - - } - - // before callback - html = this.callback('syncBefore', false, html); - - this.$source.val(html); - this.setFullpageDoctype(); - - // onchange & after callback - this.callback('syncAfter', false, html); - - if (this.start === false) - { - - if (typeof e != 'undefined') - { - switch(e.which) - { - case 37: // left - break; - case 38: // up - break; - case 39: // right - break; - case 40: // down - break; - - default: this.callback('change', false, html); - } - } - else - { - this.callback('change', false, html); - } - } - - }, - syncClean: function(html) - { - if (!this.opts.fullpage) html = this.cleanStripTags(html); - - // trim - html = $.trim(html); - - // removeplaceholder - html = this.placeholderRemoveFromCode(html); - - // remove space - html = html.replace(/​/gi, ''); - html = html.replace(/​/gi, ''); - html = html.replace(/<\/a> /gi, '<\/a> '); - html = html.replace(/\u200B/g, ''); - - if (html == '

' || html == '

' || html == '

 

') - { - html = ''; - } - - // link nofollow - if (this.opts.linkNofollow) - { - html = html.replace(//gi, ''); - html = html.replace(//gi, ''); - } - - // php code fix - html = html.replace('', '?>'); - - // revert no editable - html = html.replace(/<(.*?)class="noeditable"(.*?) contenteditable="false"(.*?)>/gi, '<$1class="noeditable"$2$3>'); - - html = html.replace(/ data-tagblock=""/gi, ''); - html = html.replace(/\n?<\/(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi, ''); - - // remove image resize - html = html.replace(/([\w\W]*?)<\/span>/gi, '$3'); - html = html.replace(/(.*?)<\/span>/gi, ''); - html = html.replace(/(.*?)<\/span>/gi, ''); - - // remove empty lists - html = html.replace(/<(ul|ol)>\s*\t*\n*<\/(ul|ol)>/gi, ''); - - // remove font - if (this.opts.cleanFontTag) - { - html = html.replace(/([\w\W]*?)<\/font>/gi, '$2'); - } - - // remove spans - html = html.replace(/([\w\W]*?)<\/span>/gi, '$2'); - html = html.replace(//gi, ''); - html = html.replace(//gi, ''); - html = html.replace(/([\w\W]*?)<\/span>/gi, ''); - - html = html.replace(//gi, ''); - - // special characters - html = html.replace(/&/gi, '&'); - html = html.replace(/\u2122/gi, '™'); - html = html.replace(/\u00a9/gi, '©'); - html = html.replace(/\u2026/gi, '…'); - html = html.replace(/\u2014/gi, '—'); - html = html.replace(/\u2010/gi, '‐'); - - html = this.cleanReConvertProtected(html); - - return html; - }, - - - - // BUILD - buildStart: function() - { - // content - this.content = ''; - - // container - this.$box = $('
'); - - // textarea test - if (this.$source[0].tagName === 'TEXTAREA') this.opts.textareamode = true; - - // mobile - if (this.opts.mobile === false && this.isMobile()) - { - this.buildMobile(); - } - else - { - // get the content at the start - this.buildContent(); - - if (this.opts.iframe) - { - // build as iframe - this.opts.autoresize = false; - this.iframeStart(); - } - else if (this.opts.textareamode) this.buildFromTextarea(); - else this.buildFromElement(); - - // options and final setup - if (!this.opts.iframe) - { - this.buildOptions(); - this.buildAfter(); - } - } - }, - buildMobile: function() - { - if (!this.opts.textareamode) - { - this.$editor = this.$source; - this.$editor.hide(); - this.$source = this.buildCodearea(this.$editor); - this.$source.val(this.content); - } - - this.$box.insertAfter(this.$source).append(this.$source); - }, - buildContent: function() - { - if (this.opts.textareamode) this.content = $.trim(this.$source.val()); - else this.content = $.trim(this.$source.html()); - }, - buildFromTextarea: function() - { - this.$editor = $('
'); - this.$box.insertAfter(this.$source).append(this.$editor).append(this.$source); - - // enable - this.buildAddClasses(this.$editor); - this.buildEnable(); - }, - buildFromElement: function() - { - this.$editor = this.$source; - this.$source = this.buildCodearea(this.$editor); - this.$box.insertAfter(this.$editor).append(this.$editor).append(this.$source); - - // enable - this.buildEnable(); - }, - buildCodearea: function($source) - { - return $('' - + '' - + '' - + '
' - + '' - + '' - + '
' - - }); - }, - modalInit: function(title, content, width, callback) - { - this.modalSetOverlay(); - - this.$redactorModalWidth = width; - this.$redactorModal = $('#redactor_modal'); - - if (!this.$redactorModal.length) - { - this.$redactorModal = $('