install tinymce 4.1.7 with Bower
refs CNVS-11523 this doesn't make use of 4.1.7, just gets it into the repo so we have a stable installed version Change-Id: Id5dd09574d1f74ec33ad373998510181deac7f28 Reviewed-on: https://gerrit.instructure.com/49996 Tested-by: Jenkins Reviewed-by: Jacob Fugal <jacob@instructure.com> QA-Review: Ethan Vizitei <evizitei@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
|
@ -19,6 +19,7 @@
|
|||
"ic-sortable": "0.0.2",
|
||||
"react": "0.12.2",
|
||||
"react-router": "0.11.6",
|
||||
"react-modal": "0.0.7"
|
||||
"react-modal": "0.0.7",
|
||||
"tinymce": "4.1.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "tinymce",
|
||||
"version": "4.1.7",
|
||||
"description": "Web based JavaScript HTML WYSIWYG editor control.",
|
||||
"license": "http://www.tinymce.com/license",
|
||||
"keywords": [
|
||||
"editor",
|
||||
"wysiwyg",
|
||||
"tinymce",
|
||||
"richtext",
|
||||
"javascript",
|
||||
"html"
|
||||
],
|
||||
"homepage": "http://www.tinymce.com",
|
||||
"main": "tinymce.min.js",
|
||||
"ignore": [
|
||||
"readme.md",
|
||||
"composer.json",
|
||||
"package.json"
|
||||
],
|
||||
"_release": "4.1.7",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "4.1.7",
|
||||
"commit": "555fcfa1a09e93732a51950ba3d731050f31d39a"
|
||||
},
|
||||
"_source": "git://github.com/tinymce/tinymce-dist.git",
|
||||
"_target": "4.1.7",
|
||||
"_originalSource": "tinymce"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
composer.json
|
||||
bower.json
|
||||
readme.md
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "tinymce",
|
||||
"version": "4.1.7",
|
||||
"description": "Web based JavaScript HTML WYSIWYG editor control.",
|
||||
"license": "http://www.tinymce.com/license",
|
||||
"keywords": [
|
||||
"editor",
|
||||
"wysiwyg",
|
||||
"tinymce",
|
||||
"richtext",
|
||||
"javascript",
|
||||
"html"
|
||||
],
|
||||
"homepage": "http://www.tinymce.com",
|
||||
"main": "tinymce.min.js",
|
||||
"ignore": [
|
||||
"readme.md",
|
||||
"composer.json",
|
||||
"package.json"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,710 @@
|
|||
Version 4.1.7 (2014-11-27)
|
||||
Added HTML5 schema support for srcset, source and picture. Patch contributed by mattheu.
|
||||
Added new cache_suffix setting to enable cache busting by producing unique urls.
|
||||
Added new paste_convert_word_fake_lists option to enable users to disable the fake lists convert logic.
|
||||
Fixed so advlist style changes adds undo levels for each change.
|
||||
Fixed bug where WebKit would sometimes produce an exception when the autolink plugin where looking for URLs.
|
||||
Fixed bug where IE 7 wouldn't be rendered properly due to to aggressive css compression.
|
||||
Fixed bug where DomQuery wouldn't accept window as constructor element.
|
||||
Fixed bug where the color picker in 3.x dialogs wouldn't work properly. Patch contributed by Callidior.
|
||||
Fixed bug where the image plugin wouldn't respect the document_base_url.
|
||||
Fixed bug where the jQuery plugin would fail to append to elements named array prototype names.
|
||||
Version 4.1.6 (2014-10-08)
|
||||
Fixed bug with clicking on the scrollbar of the iframe would cause a JS error to be thrown.
|
||||
Fixed bug where null would produce an exception if you passed it to selection.setRng.
|
||||
Fixed bug where Ctrl/Cmd+Tab would indent the current list item if you switched tabs in the browser.
|
||||
Fixed bug where pasting empty cells from Excel would result in a broken table.
|
||||
Fixed bug where it wasn't possible to switch back to default list style type.
|
||||
Fixed issue where the select all quirk fix would fire for other modifiers than Ctrl/Cmd combinations.
|
||||
Replaced jake with grunt since it is more mainstream and has better plugin support.
|
||||
Version 4.1.5 (2014-09-09)
|
||||
Fixed bug where sometimes the resize rectangles wouldn't properly render on images on WebKit/Blink.
|
||||
Fixed bug in list plugin where delete/backspace would merge empty LI elements in lists incorrectly.
|
||||
Fixed bug where empty list elements would result in empty LI elements without it's parent container.
|
||||
Fixed bug where backspace in empty caret formated element could produce an type error exception of Gecko.
|
||||
Fixed bug where lists pasted from word with a custom start index above 9 wouldn't be properly handled.
|
||||
Fixed bug where tabfocus plugin would tab out of the editor instance even if the default action was prevented.
|
||||
Fixed bug where tabfocus wouldn't tab properly to other adjacent editor instances.
|
||||
Fixed bug where the DOMUtils setStyles wouldn't properly removed or update the data-mce-style attribute.
|
||||
Fixed bug where dialog select boxes would be placed incorrectly if document.body wasn't statically positioned.
|
||||
Fixed bug where pasting would sometimes scroll to the top of page if the user was using the autoresize plugin.
|
||||
Fixed bug where caret wouldn't be properly rendered by Chrome when clicking on the iframes documentElement.
|
||||
Fixed so custom images for menubutton/splitbutton can be provided. Patch contributed by Naim Hammadi.
|
||||
Fixed so the default action of windows closing can be prevented by blocking the default action of the close event.
|
||||
Fixed so nodeChange and focus of the editor isn't automatically performed when opening sub dialogs.
|
||||
Version 4.1.4 (2014-08-21)
|
||||
Added new media_filter_html option to media plugin that blocks any conditional comments, scripts etc within a video element.
|
||||
Added new content_security_policy option allows you to set custom policy for iframe contents. Patch contributed by Francois Chagnon.
|
||||
Fixed bug where activate/deactivate events wasn't firing properly when switching between editors.
|
||||
Fixed bug where placing the caret on iOS was difficult due to a WebKit bug with touch events.
|
||||
Fixed bug where the resize helper wouldn't render properly on older IE versions.
|
||||
Fixed bug where resizing images inside tables on older IE versions would sometimes fail depending mouse position.
|
||||
Fixed bug where editor.insertContent would produce an exception when inserting select/option elements.
|
||||
Fixed bug where extra empty paragraphs would be produced if block elements where inserted inside span elements.
|
||||
Fixed bug where the spellchecker menu item wouldn't be properly checked if spell checking was started before it was rendered.
|
||||
Fixed bug where the DomQuery filter function wouldn't remove non elements from collection.
|
||||
Fixed bug where document with custom document.domain wouldn't properly render the editor.
|
||||
Fixed bug where IE 8 would throw exception when trying to enter invalid color values into colorboxes.
|
||||
Fixed bug where undo manager could incorrectly add an extra undo level when custom resize handles was removed.
|
||||
Fixed bug where it wouldn't be possible to alter cell properties properly on table cells on IE 8.
|
||||
Fixed so the color picker button in table dialog isn't shown unless you include the colorpicker plugin or add your own custom color picker.
|
||||
Fixed so activate/deactivate events fire when windowManager opens a window since.
|
||||
Fixed so the table advtab options isn't separated by an underscore to normalize naming with image_advtab option.
|
||||
Fixed so the table cell dialog has proper padding when the advanced tab in disabled.
|
||||
Version 4.1.3 (2014-07-29)
|
||||
Added event binding logic to tinymce.util.XHR making it possible to override headers and settings before any request is made.
|
||||
Fixed bug where drag events wasn't fireing properly on older IE versions since the event handlers where bound to document.
|
||||
Fixed bug where drag/dropping contents within the editor on IE would force the contents into plain text mode even if it was internal content.
|
||||
Fixed bug where IE 7 wouldn't open menus properly due to a resize bug in the browser auto closing them immediately.
|
||||
Fixed bug where the DOMUtils getPos logic wouldn't produce a valid coordinate inside the body if the body was positioned non static.
|
||||
Fixed bug where the element path and format state wasn't properly updated if you had the wordcount plugin enabled.
|
||||
Fixed bug where a comment at the beginning of source would produce an exception in the formatter logic.
|
||||
Fixed bug where setAttrib/getAttrib on null would throw exception together with any hooked attributes like style.
|
||||
Fixed bug where table sizes wasn't properly retained when copy/pasting on WebKit/Blink.
|
||||
Fixed bug where WebKit/Blink would produce colors in RGB format instead of the forced HEX format when deleting contents.
|
||||
Fixed bug where the width attribute wasn't updated on tables if you changed the size inside the table dialog.
|
||||
Fixed bug where control selection wasn't properly handled when the caret was placed directly after an image.
|
||||
Fixed bug where selecting the contents of table cells using the selection.select method wouldn't place the caret properly.
|
||||
Fixed bug where the selection state for images wasn't removed when placing the caret right after an image on WebKit/Blink.
|
||||
Fixed bug where all events wasn't properly unbound when and editor instance was removed or destroyed by some external innerHTML call.
|
||||
Fixed bug where it wasn't possible or very hard to select images on iOS when the onscreen keyboard was visible.
|
||||
Fixed so auto_focus can take a boolean argument this will auto focus the last initialized editor might be useful for single inits.
|
||||
Fixed so word auto detect lists logic works better for faked lists that doesn't have specific markup.
|
||||
Fixed so nodeChange gets fired on mouseup as it used to before 4.1.1 we optimized that event to fire less often.
|
||||
Removed the finish menu item from spellchecker menu since it's redundant you can stop spellchecking by toggling menu item or button.
|
||||
Version 4.1.2 (2014-07-15)
|
||||
Added offset/grep to DomQuery class works basically the same as it's jQuery equivalent.
|
||||
Fixed bug where backspace/delete or setContent with an empty string would remove header data when using the fullpage plugin.
|
||||
Fixed bug where tinymce.remove with a selector not matching any editors would remove all editors.
|
||||
Fixed bug where resizing of the editor didn't work since the theme was calling setStyles instead of setStyle.
|
||||
Fixed bug where IE 7 would fail to append html fragments to iframe document when using DomQuery.
|
||||
Fixed bug where the getStyle DOMUtils method would produce an exception if it was called with null as it's element.
|
||||
Fixed bug where the paste plugin would remove the element if the none of the paste_webkit_styles rules matched the current style.
|
||||
Fixed bug where contextmenu table items wouldn't work properly on IE since it would some times fire an incorrect selection change.
|
||||
Fixed bug where the padding/border values wasn't used in the size calculation for the body size when using autoresize. Patch contributed by Matt Whelan.
|
||||
Fixed bug where conditional word comments wouldn't be properly removed when pasting plain text.
|
||||
Fixed bug where resizing would sometime fail on IE 11 when the mouseup occurred inside the resizable element.
|
||||
Fixed so the iframe gets initialized without any inline event handlers for better CSP support. Patch contributed by Matt Whelan.
|
||||
Fixed so the tinymce.dom.Sizzle is the latest version of sizzle this resolves the document context bug.
|
||||
Version 4.1.1 (2014-07-08)
|
||||
Fixed bug where pasting plain text on some WebKit versions would result in an empty line.
|
||||
Fixed bug where resizing images inside tables on IE 11 wouldn't work properly.
|
||||
Fixed bug where IE 11 would sometimes throw "Invalid argument" exception when editor contents was set to an empty string.
|
||||
Fixed bug where document.activeElement would throw exceptions on IE 9 when that element was hidden or removed from dom.
|
||||
Fixed bug where WebKit/Blink sometimes produced br elements with the Apple-interchange-newline class.
|
||||
Fixed bug where table cell selection wasn't properly removed when copy/pasting table cells.
|
||||
Fixed bug where pasting nested list items from Word wouldn't produce proper semantic nested lists.
|
||||
Fixed bug where right clicking using the contextmenu plugin on WebKit/Blink on Mac OS X would select the target current word or line.
|
||||
Fixed bug where it wasn't possible to alter table cell properties on IE 8 using the context menu.
|
||||
Fixed bug where the resize helper wouldn't be correctly positioned on older IE versions.
|
||||
Fixed bug where fullpage plugin would produce an error if you didn't specify a doctype encoding.
|
||||
Fixed bug where anchor plugin would get the name/id of the current element even if it wasn't anchor element.
|
||||
Fixed bug where visual aids for tables wouldn't be properly disabled when changing the border size.
|
||||
Fixed bug where some control selection events wasn't properly fired on older IE versions.
|
||||
Fixed bug where table cell selection on older IE versions would prevent resizing of images.
|
||||
Fixed bug with paste_data_images paste option not working properly on modern IE versions.
|
||||
Fixed bug where custom elements with underscores in the name wasn't properly parsed/serialized.
|
||||
Fixed bug where applying inline formats to nested list elements would produce an incorrect formatting result.
|
||||
Fixed so it's possible to hide items from elements path by using preventDefault/stopPropagation.
|
||||
Fixed so inline mode toolbar gets rendered right aligned if the editable element positioned to the documents right edge.
|
||||
Fixed so empty inline elements inside empty block elements doesn't get removed if configured to be kept intact.
|
||||
Fixed so DomQuery parentsUntil/prevUntil/nextUntil supports selectors/elements/filters etc.
|
||||
Fixed so legacyoutput plugin overrides fontselect and fontsizeselect controls and handles font elements properly.
|
||||
Version 4.1.0 (2014-06-18)
|
||||
Added new file_picker_callback option to replace the old file_browser_callback the latter will still work though.
|
||||
Added new custom colors to textcolor plugin will be displayed if a color picker is provided also shows the latest colors.
|
||||
Added new color_picker_callback option to enable you to add custom color pickers to the editor.
|
||||
Added new advanced tabs to table/cell/row dialogs to enable you to select colors for border/background.
|
||||
Added new colorpicker plugin that lets you select colors from a hsv color picker.
|
||||
Added new tinymce.util.Color class to handle color parsing and converting.
|
||||
Added new colorpicker UI widget element lets you add a hsv color picker to any form/window.
|
||||
Added new textpattern plugin that allows you to use markdown like text patterns to format contents.
|
||||
Added new resize helper element that shows the current width & height while resizing.
|
||||
Added new "once" method to Editor and EventDispatcher enables since callback execution events.
|
||||
Added new jQuery like class under tinymce.dom.DomQuery it's exposed on editor instances (editor.$) and globally under (tinymce.$).
|
||||
Fixed so the default resize method for images are proportional shift/ctrl can be used to make an unproportional size.
|
||||
Fixed bug where the image_dimensions option of the image plugin would cause exceptions when it tried to update the size.
|
||||
Fixed bug where table cell dialog class field wasn't properly updated when editing an a table cell with an existing class.
|
||||
Fixed bug where Safari on Mac would produce webkit-fake-url for pasted images so these are now removed.
|
||||
Fixed bug where the nodeChange event would get fired before the selection was changed when clicking inside the current selection range.
|
||||
Fixed bug where valid_classes option would cause exception when it removed internal prefixed classes like mce-item-.
|
||||
Fixed bug where backspace would cause navigation in IE 8 on an inline element and after a caret formatting was applied.
|
||||
Fixed so placeholder images produced by the media plugin gets selected when inserted/edited.
|
||||
Fixed so it's possible to drag in images when the paste_data_images option is enabled. Might be useful for mail clients.
|
||||
Fixed so images doesn't get a width/height applied if the image_dimensions option is set to false useful for responsive contents.
|
||||
Fixed so it's possible to pass in an optional arguments object for the nodeChanged function to be passed to all nodechange event listeners.
|
||||
Fixed bug where media plugin embed code didn't update correctly.
|
||||
Version 4.0.28 (2014-05-27)
|
||||
Fixed critical issue with empty urls producing an exception when converted into absolute urls due to resent bug fix in tinymce.util.URI.
|
||||
Version 4.0.27 (2014-05-27)
|
||||
Added support for definition lists to lists plugin and enter key logic. This can now created by the format menu.
|
||||
Added cmd option for the style_formats menu enables you to toggle commands on/off using the formats menu for example lists.
|
||||
Added definition lists to visualblocks plugin so these are properly visualized like other list elements.
|
||||
Added new paste_merge_formats option that reduces the number of nested text format elements produced on paste. Enabled by default.
|
||||
Added better support for nested link_list/image_list menu items each item can now have a "menu" item with subitems.
|
||||
Added "Add to Dictionary" support to spellchecker plugin when the backend tells that this feature is available.
|
||||
Added new table_default_attributes/table_default_styles options patch contributed by Dan Villiom Podlaski Christiansen.
|
||||
Added new table_class_list/table_cell_class_list/table_row_class_list options to table plugin.
|
||||
Added new invalid_styles/valid_classes options to better control what gets returned for the style/class attribute.
|
||||
Added new file_browser_callback_types option that allows you to specify where to display the picker based on dialog type.
|
||||
Fixed so the selected state is properly handled on nested menu items in listboxes patch contributed by Jelle Kralt.
|
||||
Fixed so the invisiblity css value for TinyMCE gets set to inherit instead of visible to better support dialog scripts like reveal.
|
||||
Fixed bug where Gecko would remove anchors when pasting since the their default built in logic removes empty nodes.
|
||||
Fixed bug where it wasn't possible to paste on Chrome Andoid since it doesn't properly support the Clipboard API yet.
|
||||
Fixed bug where user defined type attribute value of text/javascript didn't get properly serialized.
|
||||
Fixed bug where space in span elements would removed when the element was considered empty.
|
||||
Fixed bug where the undo/redo button states didn't change if you removed all undo levels using undoManager.clear.
|
||||
Fixed bug where unencoded links inside query strings or hash values would get processed by the relative urls logic.
|
||||
Fixed bug where contextmenu would automatically close in inline editing mode on Firefox running on Mac.
|
||||
Fixed bug where Gecko/IE would produce multiple BR elements when forced_root_block was set to false and a table was the last child of body.
|
||||
Fixed bug where custom queryCommandState handlers didn't properly handle boolean states.
|
||||
Fixed bug where auto closing float panels link menus wasn't automatically closed when the window was resized.
|
||||
Fixed bug where the image plugin wouldn't update image dimensions when the current image was changed using the image_list select box.
|
||||
Fixed bug with paste plugin not properly removing paste bin on Safari Mac when using the cmd+shift+v keyboard command.
|
||||
Fixed bug where the paste plugin wouln't properly strip trailing br elements under very specific scenarios.
|
||||
Fixed bug where enter key wouldn't properly place the caret on Gecko when pressing enter in a text block with a br ended line inside.
|
||||
Fixed bug where Safari Mac shortcuts like Cmd+Opt+L didn't get passed through to the browser due to a Quirks fix.
|
||||
Fixed so plain text mode works better when it converts rich text to plain text when pasting from for example Word.
|
||||
Fixed so numeric keycodes can be used in the shortcut format enabling support for any key to be specified.
|
||||
Fixed so table cells can be navigated with tab key and new rows gets automatically added when you are at the last cell.
|
||||
Fixed bug where formatting before cursor gets removed when toggled off for continued content.
|
||||
Version 4.0.26 (2014-05-06)
|
||||
Fixed bug in media plugin where changing existing url did not use media regex patterns to create protocol neutral url.
|
||||
Fixed bug where selection wasn't properly restored on IE 11 due to a browser bug with Element.contains.
|
||||
Version 4.0.25 (2014-04-30)
|
||||
Fixed bug where it wasn't possible to submit forms with editor instances on WebKit/Blink.
|
||||
Version 4.0.24 (2014-04-30)
|
||||
Added new event_root setting for inline editors. Lets you bind all editor events on a parent container.
|
||||
Fixed bug where show/hide/isHidden didn't work properly for inline editor instances.
|
||||
Fixed bug where preview plugin dialog didn't handle relative urls properly.
|
||||
Fixed bug where the autolink plugin would remove the trailing space after an inserted link.
|
||||
Fixed bug in paste plugin where pasting in a page with scrollbars would scroll to top of page in webkit browsers.
|
||||
Fixed bug where the paste plugin on WebKit would remove styles from pasted source code with style attributes.
|
||||
Fixed so image_list/link_list can be a function that allows custom async calls to populate these lists.
|
||||
Version 4.0.23 (2014-04-24)
|
||||
Added isSameOrigin method to tinymce.util.URI it handles default protocol port numbers better. Patch contributed by Matt Whelan.
|
||||
Fixed bug where IE 11 would add br elements to the end of the editor body element each time it was shown/hidden.
|
||||
Fixed bug where the autolink plugin would produce an index out of range exception for some very specific HTML.
|
||||
Fixed bug where the charmap plugin wouldn't properly insert non breaking space characters when selected.
|
||||
Fixed bug where pasting from Excel 2011 on Mac didn't produce a proper table when using the paste plugin.
|
||||
Fixed bug where drag/dropping inside a table wouldn't properly end the table cell selection.
|
||||
Fixed bug where drag/dropping images within tables on Safari on Mac wouldn't work properly.
|
||||
Fixed bug where editors couldn't be re-initialized if they where externally destroyed.
|
||||
Fixed bug where inline editors would produce a range index exception when clicking on buttons like bold.
|
||||
Fixed bug where the preview plugin wouldn't properly handle non encoded upper UTF-8 characters.
|
||||
Fixed so document.currentScript is used when detecting the current script location. Patch contributed by Mickael Desgranges.
|
||||
Fixed issue with the paste_webkit_styles option so is disabled by default since it might produce a lot of extra styles.
|
||||
Version 4.0.22 (2014-04-16)
|
||||
Added lastLevel to BeforeAddUndo level event so it's easier to block undo level creation based.
|
||||
Fixed so multiple list elements can be indented properly. Patch contributed by Dan Villiom Podlaski Christiansen.
|
||||
Fixed bug where the selection would be at the wrong location sometimes for inline editor instances.
|
||||
Fixed bug where drag/dropping content into an inline editor would fail on WebKit/Blink.
|
||||
Fixed bug where table grid wouldn't work properly when the UI was rendered in for RTL mode.
|
||||
Fixed bug where range normalization wouldn't handle mixed contentEditable nodes properly.
|
||||
Fixed so the media plugin doesn't override the existing element rules you now need to manually whitelist non standard attributes.
|
||||
Fixed so old language packs get properly loaded when the new longer language code format is used.
|
||||
Fixed so all track changes junk such as comments, deletes etc gets removed when pasting from Word.
|
||||
Fixed so non image data urls is blocked by default since they might contain scripts.
|
||||
Fixed so it's possible to import styles from the current page stylesheets into an inline editor by using the importcss_file_filter.
|
||||
Fixed bug where the spellchecker plugin wouldn't add undo levels for each suggestion replacement.
|
||||
Reworked the default spellchecker RPC API to match the new PHP Spellchecker package. Fallback documented in the TinyMCE docs.
|
||||
Version 4.0.21 (2014-04-01)
|
||||
Added new getCssText method to formatter to get the preview css text value for a format to be used in UI.
|
||||
Added new table_grid option that allows you to disable the table grid and use a dialog.
|
||||
Added new image_description, image_dimensions options to image plugin. Patch contributed by Pat O'Neill.
|
||||
Added new media_alt_source, media_poster, media_dimensions options to media plugin. Patch contributed by Pat O'Neill.
|
||||
Added new ability to specify high/low dpi versions custom button images for retina displays.
|
||||
Added new getWindows method to WindowManager makes it easier to control the currently opened windows.
|
||||
Added new paste_webkit_styles option to paste plugin to control the styles that gets retained on WebKit.
|
||||
Added preview of classes for the selectboxes used by the link_class_list/image_class_list options.
|
||||
Added support for Sauce Labs browser testing using the new saucelabs-tests build target.
|
||||
Added title input field to link dialog for a11y reasons can be disabled by using the link_title option.
|
||||
Fixed so the toolbar option handles an array as input for multiple toolbar rows.
|
||||
Fixed so the editor renders in XHTML mode apparently some people still use this rendering mode.
|
||||
Fixed so icons gets rendered better on Firefox on Mac OS X by applying -moz-osx-font-smoothing.
|
||||
Fixed so the auto detected external media sources produced protocol relative urls. Patch contributed by Pat O'Neill.
|
||||
Fixed so it's possible to update the text of a button after it's been rendered to page DOM.
|
||||
Fixed bug where iOS 7.1 Safari would open linked when images where inserted into links.
|
||||
Fixed bug where IE 11 would scroll to the top of inline editable elements when applying formatting.
|
||||
Fixed bug where tabindex on elements within the editor contents would cause issues on some browsers.
|
||||
Fixed bug where link text wouldn't be properly updated in gecko if you changed an existing link.
|
||||
Fixed bug where it wasn't possible to close dialogs with the escape key if the focus was inside a textbox.
|
||||
Fixed bug where Gecko wouldn't paste rich text contents from Word or other similar word processors.
|
||||
Fixed bug where binding events after the control had been rendered could fail to produce a valid delegate.
|
||||
Fixed bug where IE 8 would throw and error when removing editors with a cross domain content_css setting.
|
||||
Fixed bug where IE 9 wouldn't be able to select text after an editor instance with caret focus was removed.
|
||||
Fixed bug where the autoresize plugin wouldn't resize the editor if you inserted huge images.
|
||||
Fixed bug where multiple calls to the same init would produce extra editor instances.
|
||||
Fixed bug where fullscreen toggle while having the autoresize plugin enabled wouldn't produce scrollbars.
|
||||
Fixed so screen readers use a dialog instead of the grid for inserting tables.
|
||||
Fixed so Office 365 Word contents gets filtered the same way as content from desktop Office.
|
||||
Fixed so it's possible to override the root container for UI elements defaults to document.body.
|
||||
Fixed bug where tabIndex is set to -1 on inline editable elements. It now keeps the existing tabIndex intact.
|
||||
Fixed issue where the UndoManager transact method couldn't be nested since it only had one lock.
|
||||
Fixed issue where headings/heading where labeled incorrectly as headers/header.
|
||||
Version 4.0.20 (2014-03-18)
|
||||
Fixed so all unit tests can be executed in a headless phantomjs instance for CI testing.
|
||||
Fixed so directionality setting gets applied to the preview dialog as well as the editor body element.
|
||||
Fixed a performance issue with the "is" method in DOMUtils. Patch contributed by Paul Bosselaar.
|
||||
Fixed bug where paste plugin wouldn't paste plain text properly when pasting using browser menus.
|
||||
Fixed bug where focusable SVG elements would throw an error since className isn't a proper string.
|
||||
Fixed bug where the preview plugin didn't properly support the document_base_url setting.
|
||||
Fixed bug where the focusedEditor wouldn't be set to null when that editor was removed.
|
||||
Fixed bug where Gecko would throw an exception when editors where removed.
|
||||
Fixed bug where the FocusManager wouldn't handle selection restoration properly on older IE versions.
|
||||
Fixed bug where the searchreplace plugin would produce an exception on very specific multiple searches.
|
||||
Fixed bug where some events wasn't properly unbound when all editors where removed from page.
|
||||
Fixed bug where tapping links on iOS 7.1 would open the link instead of placing the caret inside.
|
||||
Fixed bug where holding the finger down on iOS 7.1 would open the link/image callout menu.
|
||||
Fixed so the jQuery plugin returns null when getting the the tinymce instance of an element before it's initialized.
|
||||
Fixed so selection normalization gets executed more often to reduce incorrect UI states on Gecko.
|
||||
Fixed so the default action of closing the window on a form submission can be prevented using "preventDefault".
|
||||
Version 4.0.19 (2014-03-11)
|
||||
Added support for CSS selector expressions in object_resizing option. Allows you to control what to resize.
|
||||
Added addToTop compatibility to compat3x plugin enables more legacy 3.x plugins to work properly.
|
||||
Fixed bug on IE where it wasn't possible to align images when they where floated left.
|
||||
Fixed bug where the indent/outdent buttons was enabled though readonly mode was enabled.
|
||||
Fixed bug where the nodeChanged event was fired when readonly mode was enabled.
|
||||
Fixed bug where events like blur could be fired to editor instances that where manually removed on IE 11.
|
||||
Fixed bug where IE 11 would move focus to menubar/toolbar when using the tab key in a form with an editor.
|
||||
Fixed bug where drag/drop in Safari on Mac didn't work properly due to lack of support for modern dataTransfer object.
|
||||
Fixed bug where the remove event wasn't properly executed when the editor instances where removed.
|
||||
Fixed bug where the selection change handler on inline editors would fail if the editor instance was removed.
|
||||
Version 4.0.18 (2014-02-27)
|
||||
Fixed bug where images would get class false/undefined when initially created.
|
||||
Version 4.0.17 (2014-02-26)
|
||||
Added much better wai-aria accessibility support when it comes to keyboard navigation of complex UI controls.
|
||||
Added dfn,code,samp,kbd,var,cite,mark,q elements to the default remove formats list. Patch contributed by Naim Hammadi.
|
||||
Added var,cite,dfn,code,mark,q,sup,sub to the list of elements that gets cloned on enter. Patch contributed by Naim Hammadi.
|
||||
Added new visual_anchor_class option to specify a custom class for inline anchors. Patch contributed by Naim Hammadi.
|
||||
Added support for paste_data_images on WebKit/Blink when the user pastes image data.
|
||||
Added support for highlighting the video icon when a video is added that produces an iframe. Patch contributed by monkeydiane.
|
||||
Added image_class_list/link_class_list options to image/link dialogs to let the user select classes.
|
||||
Fixed bug where the ObjectResizeStart event didn't get fired properly by the ControlSelection class.
|
||||
Fixed bug where the autolink plugin would steal focus when loaded on IE 9+.
|
||||
Fixed bug where the editor save method would remove the current selection when called on an inline editor.
|
||||
Fixed bug where the formatter would merge span elements with parent bookmarks if an id format was used.
|
||||
Fixed bug where WebKit/Blink browsers would scroll to the top of the editor when pasting into an empty element.
|
||||
Fixed bug where removing the editor would cause an error about wrong document on IE 11 under specific circumstances.
|
||||
Fixed bug where Gecko would place the caret at an incorrect location when using backspace.
|
||||
Fixed bug where Gecko would throw "Wrong Document Error" for ranges that pointing to removed nodes.
|
||||
Fixed bug where it wasn't possible to properly update the title and encoding properties in the fullpage plugin.
|
||||
Fixed bug where paste plugin would produce an extra undo level on IE.
|
||||
Fixed bug where the formatter would apply inline formatting outside the current word in if the selection was collapsed.
|
||||
Fixed bug where it wasn't possible to delete tables on Chrome if you placed the selection within all the contents of the table.
|
||||
Fixed bug where older IE versions wouldn't properly insert contents into table cells when editor focus was lost.
|
||||
Fixed bug where older IE versions would fire focus/blur events even though the editor focus didn't change.
|
||||
Fixed bug where IE 11 would add two trailing BR elements to the editor iframe body if the editor was hidden.
|
||||
Fixed bug where the visualchars plugin wouldn't display non breaking spaces if they where inserted while the state was enabled.
|
||||
Fixed bug where the wordcount plugin would be very slow some HTML where to much backtracking occurred.
|
||||
Fixed so pagebreak elements in the editor breaks pages when printing. Patch contributed by penc.
|
||||
Fixed so UndoManager events pass though the original event that created the undo level such as a keydown, blur etc.
|
||||
Fixed so the inserttime button is callsed insertdatetime the same as the menu item and plugin name.
|
||||
Fixed so the word count plugin handles counting properly on most languages on the planet.
|
||||
Fixed bug where the auroreize plugin would throw an error if the editor was manually removed within a few seconds.
|
||||
Fixed bug where the image dialog would get stuck if the src was removed. Patch contribued by monkeydiane.
|
||||
Fixed bug where there is an extra br tag for IE 9/10 that isn't needed. Patch contributed by monkeydiane.
|
||||
Fixed bug where drag/drop in a scrolled editor would fail since it didn't use clientX/clientY cordinates. Patch contributed by annettem.
|
||||
Version 4.0.16 (2014-01-31)
|
||||
Fixed bug where the editor wouldn't be properly rendered on IE 10 depending on the document.readyState.
|
||||
Version 4.0.15 (2014-01-31)
|
||||
Fixed bug where paste in inline mode would produce an exception if the contents was pasted inside non overflow element.
|
||||
Version 4.0.14 (2014-01-30)
|
||||
Fixed a bug in the image plugin where images couldn't be inserted if the image_advtab option wasn't set to true.
|
||||
Version 4.0.13 (2014-01-30)
|
||||
Added language selection menu to spellchecker button similar to the 3.x functionality. Patch contributed by threebytesfull.
|
||||
Added new style_formats_merge option that enables you to append to the default formats instead of replaceing them. Patch contributed by PacificMorrowind.
|
||||
Fixed bug where the DOMUtils getPos API function didn't properly handle the location of the root element. Patch contributed by Andrew Ozz.
|
||||
Fixed bug where the spellchecker wouldn't properly place the spellchecker suggestions menu. Patch contributed by Andrew Ozz.
|
||||
Fixed bug where the tabfocus plugin would prevent the user from suing Ctrl+Tab, Patch contributed by Andrew Ozz.
|
||||
Fixed bug where table resize handles could sometimes be added to elements out side the editable inline element.
|
||||
Fixed bug where the inline mode editor UI would render incorrectly when the stylesheets didn't finish loading on Chrome.
|
||||
Fixed bug where IE 8 would insert the image outside the editor unless it was focused first.
|
||||
Fixed bug where older IE versions would throw an exception on drag/drop since they don't support modern dataTransfer API.
|
||||
Fixed bug where the blockquote button text wasn't properly translated since it had the wrong English key.
|
||||
Fixed bug where the importcss plugin didn't import a.class rules properly as selector formats.
|
||||
Fixed bug where the combobox control couldn't be disabled or set to a specific character size initially.
|
||||
Fixed bug where the FormItem didn't inherit the disabled state from the control to be wrapped.
|
||||
Fixed bug where adding a TinyMCE instance within a TinyMCE dialog wouldn't properly delegate the events.
|
||||
Fixed bug where any overflow parent containers would automatically scroll to the left when pasting in Chrome.
|
||||
Fixed bug where IE could throw an error when search/replacing contents due to an invalid selection being returned.
|
||||
Fixed bug where WebKit would fire focus/blur events incorrectly if the editor was empty due to a WebKit focus bug.
|
||||
Fixed bug where WebKit/Blink would scroll to the top of editor if the height was more than the viewport height.
|
||||
Fixed bug where blurring and removing the editor could cause an exteption to be thrown by the FocusManager.
|
||||
Fixed bug where the media plugin would override specified dimensions for url pattern matches. Patch contributed by penc.
|
||||
Fixed bug where the autoresize plugin wouldn't take margins into account when calculating the body size. Patch contributed by lepoltj.
|
||||
Fixed bug where the image plugin would throw errors some times on IE 8 when it preloaded the image to get it's dimensions.
|
||||
Fixed bug where the image plugin wouldn't update the style if the user closed the dialog before focusing out. Patch contributed by jonparrott.
|
||||
Fixed bug where bindOnReady in EventUtils wouldn't work properly for some edge cases on older IE versions. Patch contributed by Godefroy.
|
||||
Fixed bug where image selector formats wasn't properly handled by the importcss plugin.
|
||||
Fixed bug where the dirty state of the editor wasn't set when editing an existing link URL.
|
||||
Fixed bug where it wasn't possible to prevent paste from happening by blocking the default behavior when the paste plugin was enabled.
|
||||
Fixed bug where text to display in the insert/edit link dialog wouldn't be properly entity encoded.
|
||||
Fixed bug where Safari 7 on Mac OS X would delete contents if you pressed Cmd+C since it passes out a charCode for the event.
|
||||
Fixed bug where bound drop events inside inline editors would get fired on all editor instances instead of the specific instance.
|
||||
Fixed bug where images outlined selection border would be clipped when the autoresize plugin was enabled.
|
||||
Fixed bug where image dimension constrains proportions wouldn't work properly if you altered a value and immediately clicked the submit button.
|
||||
Fixed so you don't need to set language option to false when specifying a custom language_url.
|
||||
Fixed so the link dialog "text to display" field gets automatically hidden if the selection isn't text contents. Patch contributed by Godefroy.
|
||||
Fixed so the none option for the target field in the link dialog gets excluded when specifiying the target_list config option.
|
||||
Fixed so outline styles are displayed by default in the formats preview. Patch contributed by nhammadi.
|
||||
Fixed so the max characters for width/height is more than 3 in the media and image dialogs.
|
||||
Fixed so the old mceSpellCheck command toggles the spellchecker on/off.
|
||||
Fixed so the setupeditor event is fired before the setup callback setting to ease up compatibility with 3.x.
|
||||
Fixed so auto url link creation in IE 9+ is disabled by default and re-enabled by the autolink plugin.
|
||||
Removed the custom scrollbars for WebKit since the default browser scrollbars looks a lot better now days.
|
||||
Version 4.0.12 (2013-12-18)
|
||||
Added new media_scripts option to the media plugin. This makes it possible to embed videos using script elements.
|
||||
Fixed bug where WebKit/Blink would produce random span elements and styles when deleting contents inside the editor.
|
||||
Fixed bug where WebKit/Blink would produce span elements out of link elements when they where removed by the unlink command.
|
||||
Fixed bug where div block formats in inline mode where applied to all paragraphs within the editor.
|
||||
Fixed bug where div blocks where marked as an active format in inline mode when doing non collapsed selections.
|
||||
Fixed bug where the importcss plugin wouldn't append styles if the style_formats option was configured.
|
||||
Fixed bug where the importcss plugin would import styles into groups multiple times for different format menus.
|
||||
Fixed bug where the paste plugin wouldn't properly remove the paste bin element on IE if a tried to paste a file.
|
||||
Fixed bug where selection normalization wouldn't properly handle cases where a range point was after a element node.
|
||||
Fixed bug where the default time format for the inserttime split button wasn't the first item in the list.
|
||||
Fixed bug where the default text for the formatselect control wasn't properly translated by the language pack.
|
||||
Fixed bug where links would be inserted incorrectly when auto detecting absolute urls/emails links in inline mode.
|
||||
Fixed bug where IE 11 would insert contents in the wrong order due to focus/blur async problems.
|
||||
Fixed bug where pasting contents on IE sometimes would place the contents at the end of the editor.
|
||||
Fixed so drag/drop on non IE browsers gets filtered by the paste plugin. IE doesn't have the necessary APIs.
|
||||
Fixed so the paste plugin better detects Word 2007 contents not marked with -mso junk.
|
||||
Fixed so image button isn't set to an active state when selecting control/media placeholder items.
|
||||
Version 4.0.11 (2013-11-20)
|
||||
Added the possibility to update button icon after it's been rendered.
|
||||
Added new autosave_prefix option allows you to set the prefix for the local storage keys.
|
||||
Added new pagebreak_split_block option to make it easier to split block elements with a page break.
|
||||
Fixed bug where IE would some times produce font elements when typing out side the body root blocks.
|
||||
Fixed bug where IE wouldn't properly use the configured root block element but instead use the a paragraph.
|
||||
Fixed bug where IE would throw a stack overflow if control selections non images was made in inline mode.
|
||||
Fixed bug where IE 8 would render an extra enter element if the contents of the editor was empty.
|
||||
Fixed bug where the caret wasn't moved to the first suitable element when updating the source.
|
||||
Fixed bug where protocol relative urls would be forced into http protocol.
|
||||
Fixed bug where internal images with data urls such as video elements would be removed by the paste_data_images option.
|
||||
Fixed bug where the autoresize plugin wouldn't properly resize the editor to initial contents some times.
|
||||
Fixed bug where the templates dialog wouldn't be properly rendered on IE 7.
|
||||
Fixed bug where updating styles in the advanced tab under the image dialog would remove the style attribute on cancel.
|
||||
Fixed bug where tinymce.full.min.js bundle script wasn't detected when looking for the tinymce root path.
|
||||
Fixed bug where the SaxParser would throw a malformed URI sequence for inproperly encoded uris.
|
||||
Fixed bug where enabling table caption wouldn't properly render the caption element on IE 10 and below.
|
||||
Fixed bug where the scrollbar would be placed to the left and on top of the text of menu items in RTL mode.
|
||||
Fixed bug where Firefox on Mac OS X would navigate forward/backward on CMD+Arrow keys.
|
||||
Fixed bug where fullscreen toggle on fixed sized editors wouldn't be properly full screened.
|
||||
Fixed bug where the unlink button would remove all links from the body element in inline mode under running in IE.
|
||||
Fixed bug where iOS wasn't able to place the caret inside an empty editor when clicking below the first line.
|
||||
Fixed so internal document anchors in Word documents are retained when pasting using the paste from word feature.
|
||||
Fixed so menu shortcuts gets rendered with the Apple command icon patch contributed by Andy Keller.
|
||||
Fixed so the CSS compression of styles like "border" is a bit better for mixed values.
|
||||
Fixed so the template_popup_width/template_popup_height option works properly in the template plugin.
|
||||
Fixed so the languages parameter for AddOnManager.requireLangPack works the same way as for 3.x.
|
||||
Fixed so the autosave plugin uses the current page path, query string and editor id as it's default prefix.
|
||||
Fixed so the fullpage plugin adds/removes any link style sheets to the current iframe document.
|
||||
Version 4.0.10 (2013-10-28)
|
||||
Added new forced_root_block_attrs option that allows you to specify attributes for the root block.
|
||||
Fixed bug where the custom resize handles didn't work properly on IE 11.
|
||||
Fixed bug where the code plugin would select all contents in IE when content was updated.
|
||||
Fixed bug where the scroll position wouldn't get applied to floating toolbars.
|
||||
Fixed bug where focusing in/out of the editor would move the caret to the top of the editor on IE 11.
|
||||
Fixed bug where the listboxes for link and image lists wasn't updated when the url/src was changed.
|
||||
Fixed bug where selection bookmark elements would be visible in the elements path list.
|
||||
Version 4.0.9 (2013-10-24)
|
||||
Added support for external template files to template plugin just set the templates option to a URL with JSON data.
|
||||
Added new allow_script_urls option. Enabled by default, trims all script urls from attributes.
|
||||
Fixed bug where IE would sometimes throw a "Permission denied" error unless the Sizzle doc was properly removed.
|
||||
Fixed bug where lists plugin would remove outer list items if inline editable element was within a LI parent.
|
||||
Fixed bug where insert table grid widget would insert a table on item to large when using a RTL language pack.
|
||||
Fixed bug where fullscreen mode wasn't rendering properly on IE 7.
|
||||
Fixed bug where resize handlers wasn't moved correctly when scrolling inline editable elements.
|
||||
Fixed bug where it wasn't possible to paste from Excel and possible other applications due to Clipboard API bugs in browsers.
|
||||
Fixed bug where Shift+Ctrl+V didn't produce a plain text paste on IE.
|
||||
Fixed bug where IE would sometimes move the selection to the a previous location.
|
||||
Fixed bug where the editor wasn't properly scrolled to the content insert location in inline mode.
|
||||
Fixed bug where some comments would be parsed as HTML by the SaxParser.
|
||||
Fixed bug where WebKit/Blink would render tables incorrectly if unapplying formats when having multiple table cells selected.
|
||||
Fixed bug where the paste_data_images option wouldn't strip all kinds of data images.
|
||||
Fixed bug where the GridLayout didn't render items correctly if the contents overflowed the layout container.
|
||||
Fixed bug where the Window wasn't properly positioned if the size of the button bar or title bar was wider than the contents.
|
||||
Fixed bug where psuedo selectors for finding UI controls didn't work properly.
|
||||
Fixed bug where resized splitbuttons would throw an exception if it didn't contain an icon.
|
||||
Fixed bug where setContent would move focus into the editor even though it wasn't active.
|
||||
Fixed bug where IE 11 would sometimes throw an "Invalid function" error when calling setActive on the body element.
|
||||
Fixed bug where the importcss plugin would import styles from CSS files not present in the content_css array.
|
||||
Fixed bug where the jQuery plugin will initialize the editors twice if the core was loaded using the script_url option.
|
||||
Fixed various bugs and issues related to indentation of OL/UL list elements.
|
||||
Fixed so IE 7 renders the classic mode buttons the same size as other browsers.
|
||||
Fixed so document.readyState is checked when loading and initializing TinyMCE manually after page load.
|
||||
Version 4.0.8 (2013-10-10)
|
||||
Added RTL support so all of the UI is rendered right to left if a language pack has a _dir property set to rtl.
|
||||
Fixed bug where layout managers wouldn't handle subpixel values properly. When for example the browser was zoomed in.
|
||||
Fixed bug where the importcss plugin wouldn't import classes from local stylesheets with remote @import rules on Gecko.
|
||||
Fixed bug where Arabic characters wouldn't be properly counted in wordcount plugin.
|
||||
Fixed bug where submit event would still fire even if it was unbound on IE 10. Now the event is simply ignored.
|
||||
Fixed bug where IE 11 would return border-image: none when getting style attributes with borders in them.
|
||||
Fixed various UI rendering issues on older IE versions.
|
||||
Fixed so readonly option renderes the editor in inline mode with all UI elements disabled and all events blocked.
|
||||
Version 4.0.7 (2013-10-02)
|
||||
Added new importcss_selector_filter option to importcss plugin. Makes it easier to select specific classes to import.
|
||||
Added new importcss_groups option to importcss plugin. Enables you separate classes into menu groups based on filters.
|
||||
Added new PastePreProcess/PastePostProcess events and reintroduced paste_preprocess/paste_postprocess paste options.
|
||||
Added new paste_word_valid_elements option lets you control what elements gets pasted when pasting from Word.
|
||||
Fixed so panelbutton is easier to use. It's now possible to set the panel contents to any container type.
|
||||
Fixed so editor.destroy calls editor.remove so that both destroy and remove can be used to remove an editor instance.
|
||||
Fixed so the searchreplace plugin doesn't move focus into the editor until you close the dialog.
|
||||
Fixed so the searchreplace plugin search for next item if you hit enter inside the dialog.
|
||||
Fixed so importcss_selector_converter callback is executed with the scope set to importcss plugin instance.
|
||||
Fixed so the default selector converter function is exposed in importcss plugin.
|
||||
Fixed issue with the tabpanel not expanding properly when the tabs where wider than the body of the panel.
|
||||
Fixed issue with the menubar option producing a JS exception if set to true.
|
||||
Fixed bug where closing a dialog with an opened listbox would cause errors if new dialogs where opened.
|
||||
Fixed bug where hidden input elements wasn't removed when inline editor instances where removed.
|
||||
Fixed bug where editors wouldn't initialize some times due to event logic not working correctly.
|
||||
Fixed bug where pre elements woudl cause searchreplace and spellchecker plugins to mark incorrect locations.
|
||||
Fixed bug where embed elements wouldn't be properly resized if they where configured in using the video_template_callback.
|
||||
Fixed bug where paste from word would remove all BR elements since it was missing in the default paste_word_valid_elements.
|
||||
Fixed bug where paste filtering wouldn't work properly on old WebKit installations pre Clipboard API.
|
||||
Fixed bug where linebreaks would be removed by paste plugin on IE since it didn't properly detect Word contents.
|
||||
Fixed bug where paste plugin would convert some Word paragraphs that looked like lists into lists.
|
||||
Fixed bug where editors wasn't properly initialized if the document.domain is set to the same as the current domain on IE.
|
||||
Fixed bug where an exception was thrown when removing an editor after opening the context menu multiple times.
|
||||
Fixed bug where paste as plain text on Gecko would add extra BR elements when pasting paragraphs.
|
||||
Version 4.0.6 (2013-09-12)
|
||||
Added new compat3x plugin that makes it possible to load most 3.x plugins. Only available in the development package.
|
||||
Added new skin_url option enables you to load local skins when using the CDN version.
|
||||
Added new theme_url option enables you to load local themes when using the CDN version.
|
||||
Added new importcss_file_filter option to importcss to enable users to specify what files to import from.
|
||||
Added new template_preview_replace_values option to template plugin to add example data for variables.
|
||||
Added image option support for addMenuItem calls. Enables you to provide a custom image for menu items.
|
||||
Fixed bug where editor.insertContent wouldn't set format and selection type on events.
|
||||
Fixed bug where inserting BR elements on IE 8 would thrown an exception when the range is at a empty text node.
|
||||
Fixed bug where outdent of single LI element within another LI would produce an empty list element OL/UL.
|
||||
Fixed bug where the bullist/numlist buttons wouldn't be deselected when deleting all contents.
|
||||
Fixed bug where toggling an empty list item off wouldn't produce a new empty block element.
|
||||
Fixed bug where it wasn't possible to apply lists to mixed text blocks and br lines.
|
||||
Fixed bug where it wasn't possible to paste contents on iOS when the paste plugin was enabled.
|
||||
Fixed bug where it wasn't possible to delete HR elements on Gecko.
|
||||
Fixed bug where scrolling and refocusing using the mouse would place the caret incorrectly on IE.
|
||||
Fixed bug where you needed to hit the empty paragraph to get editor focus in IE 11.
|
||||
Fixed bug where activeEditor wasn't set to the correct editor when opening windows.
|
||||
Fixed bug where dirty state wasn't set to false when undoing to the first undo level.
|
||||
Fixed bug where pasting in inline mode on Safari on Mac wouldn't work properly.
|
||||
Fixed bug where content_css wasn't loaded into the insert template dialog.
|
||||
Fixed bug where setting the contents of the editor to non text contents would produce an incorrect selection range.
|
||||
Fixed so code dialog height gets smaller that the viewport height if it doesn't fit.
|
||||
Fixed so inline editable regions scroll when pressing enter/return.
|
||||
Fixed so inline toolbar gets positioned correctly when inline element is within a scrollable container.
|
||||
Fixed various memory leaks when removing editor instances dynamically.
|
||||
Removed CSS for BR elements in visualblocks due to problems with Chrome and IE.
|
||||
Version 4.0.5 (2013-08-27)
|
||||
Added visuals for UL, LI and BR to visualblocks plugin. Patch contributed by Dan Ransom.
|
||||
Added new autosave_restore_when_empty option to autosave plugin. Enabled by default.
|
||||
Fixed bug where an exception was thrown when inserting images if valid_elements didn't include an ID for the image.
|
||||
Fixed bug where the advlist plugin wouldn't properly render the splitbutton controls.
|
||||
Fixed bug where visual blocks menu item wouldn't be marked checked when using the visualblocks_default_state option.
|
||||
Fixed bug where save button in save plugin wouldn't get properly enabled when contents was changed.
|
||||
Fixed bug where it was possible to insert images without any value for it's source attribute.
|
||||
Fixed bug where altering image attributes wouldn't add a new undo level.
|
||||
Fixed bug where import rules in CSS files wouldn't be properly imported by the importcss plugin.
|
||||
Fixed bug where selectors could be imported multiple times. Producing duplicate formats.
|
||||
Fixed bug where IE would throw exception if selection was changed while the editor was hidden.
|
||||
Fixed so complex rules like .class:before doesn't get imported by default in the importcss plugin.
|
||||
Fixed so it's possible to remove images by setting the src attribute to a blank value.
|
||||
Fixed so the save_enablewhendirty setting in the save plugin is enabled by default.
|
||||
Fixed so block formats drop down for classic mode can be translated properly using language packs.
|
||||
Fixed so hr menu item and toolbar button gets the same translation string.
|
||||
Fixed so bullet list toolbar button gets the correct translation from language packs.
|
||||
Fixed issue with Chrome logging CSS warning about border styling for combo boxes.
|
||||
Fixed issue with Chrome logging warnings about deprecated keyLocation property.
|
||||
Fixed issue where custom_elements would not remove the some of the default rules when cloning rules from div and span.
|
||||
Version 4.0.4 (2013-08-21)
|
||||
Added new importcss plugin. Lets you auto import classes from CSS files similar to the 3.x behavior.
|
||||
Fixed bug where resize handles would be positioned incorrectly when inline element parent was using position: relative.
|
||||
Fixed bug where IE 8 would throw Unknown runtime error if the editor was placed within a P tag.
|
||||
Fixed bug where removing empty lists wouldn't produce blocks or brs where the old list was in the DOM.
|
||||
Fixed bug where IE 10 wouldn't properly initialize template dialog due to async loading issues.
|
||||
Fixed bug where autosave wouldn't properly display the warning about content not being saved due to isDirty changes.
|
||||
Fixed bug where it wouldn't be possible to type if a touchstart event was bound to the parent document.
|
||||
Fixed bug where code dialog in code plugin wouldn't wouldn't add a proper undo level.
|
||||
Fixed issue where resizing the editor in vertical mode would set the iframe width to a pixel value.
|
||||
Fixed issue with naming of insertdatetime settings. All are now prefixed with the plugin name.
|
||||
Fixed so an initial change event is fired when the user types the first character into the editor.
|
||||
Fixed so swf gets mapped to object element in media plugin. Enables embedding of flash with alternative poster.
|
||||
Version 4.0.3 (2013-08-08)
|
||||
Added new code_dialog_width/code_dialog_height options to control code dialog size.
|
||||
Added missing pastetext button that works the same way as the pastetext menu item.
|
||||
Added missing smaller browse button for the classical smaller toolbars.
|
||||
Fixed bug where input method would produce new lines when inserting contents to an empty editor.
|
||||
Fixed bug where pasting single indented list items from Word would cause a JS exception.
|
||||
Fixed bug where applying block formats inside list elements in inline mode would apply them to whole document.
|
||||
Fixed bug where link editing in inline mode would cause exception on IE/WebKit.
|
||||
Fixed bug where IE 10 wouldn't render the last button group properly in inline mode due to wrapping.
|
||||
Fixed bug where localStorage initialization would fail on Firefox/Chrome with disabled support.
|
||||
Fixed bug where image elements would get an __mce id when undo/redo:ing to a level with image changes.
|
||||
Fixed bug where too long template names wouldn't fit the listbox in template plugin.
|
||||
Fixed bug where alignment format options would be marked disabled when forced_root_block was set to false.
|
||||
Fixed bug where UI listboxes such as fontsize, fontfamily wouldn't update properly when switching editors in inline mode.
|
||||
Fixed bug where the formats select box would mark the editable container DIV as a applied format in inline mode.
|
||||
Fixed bug where IE 7/8 would scroll to empty editors when initialized.
|
||||
Fixed bug where IE 7/8 wouldn't display previews of format options.
|
||||
Fixed bug where UI states wasn't properly updated after code was changed in the code dialog.
|
||||
Fixed bug with setting contents in IE would select all contents within the editor.
|
||||
Fixed so the undoManages transact function disables any other undo levels from being added while within the transaction.
|
||||
Fixed so sub/sup elements gets removed when the Clear formatting action is executed.
|
||||
Fixed so text/javascript type value get removed by default from script elements to match the HTML5 spec.
|
||||
Version 4.0.2 (2013-07-18)
|
||||
Fixed bug where formatting using menus or toolbars wasn't possible on Opera 12.15.
|
||||
Fixed bug where IE 8 keyboard input would break after paste using the paste plugin.
|
||||
Fixed bug where IE 8 would throw an error when populating image size in image dialog.
|
||||
Fixed bug where image resizing wouldn't work properly on latest IE 10.0.9 version.
|
||||
Fixed bug where focus wasn't moved to the hovered menu button in a menubar container.
|
||||
Fixed bug where paste would produce an extra uneeded undo level on IE and Gecko.
|
||||
Fixed so anchors gets listed in the link dialog as they where in TinyMCE 3.x.
|
||||
Fixed so sub, sup and strike though gets passed through when pasting from Word.
|
||||
Fixed so Ctrl+P can be used to print the current document. Patch contributed by jashua212.
|
||||
Version 4.0.1 (2013-06-26)
|
||||
Added new paste_as_text config option to force paste as plaintext mode.
|
||||
Added new pastetext menu item that lets you toggle paste as plain text mode on/off.
|
||||
Added new insertdatetime_element option to insertdatetime plugin. Enables HTML5 time element support.
|
||||
Added new spellchecker_wordchar_pattern option to allow configuration of language specific characters.
|
||||
Added new marker to formats menu displaying the formats used at the current selection/caret location.
|
||||
Fixed bug where the position of the text color picker would be wrong if you switched to fullscreen.
|
||||
Fixed bug where the link plugin would ask to add the mailto: prefix multiple times.
|
||||
Fixed bug where list outdent operation could produce empty list elements on specific selections.
|
||||
Fixed bug where element path wouldn't properly select parent elements on IE.
|
||||
Fixed bug where IE would sometimes throw an exception when extrancting the current selection range.
|
||||
Fixed bug where line feeds wasn't properly rendered in source view on IE.
|
||||
Fixed bug where word count wouldn't be properly rendered on IE 7.
|
||||
Fixed bug where menubuttons/listboxes would have an incorrect height on IE 7.
|
||||
Fixed bug where browser spellchecking was enabled while editing inline on IE 10.
|
||||
Fixed bug where spellchecker wouldn't properly find non English words.
|
||||
Fixed bug where deactivating inline editor instances would force padding-top: 0 on page body.
|
||||
Fixed bug where jQuery would initialize editors multiple times since it didn't check if the editor already existed.
|
||||
Fixed bug where it wasn't possible to paste contents on IE 10 in modern UI mode when paste filtering was enabled.
|
||||
Fixed bug where tabfocus plugin wouldn't work properly on inline editor instances.
|
||||
Fixed bug where fullpage plugin would clear the existing HTML head if contents where inserted into the editor.
|
||||
Fixed bug where deleting all table rows/columns in a table would cause an exception to be thrown on IE.
|
||||
Fixed so color button panels gets toggled on/off when activated/deactivated.
|
||||
Fixed so format menu items that can't be applied to the current selection gets disabled.
|
||||
Fixed so the icon parameter for addButton isn't automatically filled if a button text is provided.
|
||||
Fixed so image size fields gets updated when selecting a new image in the image dialog.
|
||||
Fixed so it doesn't load any language pack if the language option is set to "en".
|
||||
Fixed so ctrl+shift+z works as an alternative redo shortcut to match a common Mac OS X shortcut.
|
||||
Fixed so it's not possible to drag/drop in images in Gecko by default when paste plugin is enabled.
|
||||
Fixed so format menu item texts gets translated using the specified language pack.
|
||||
Fixed so the image dialog title is the same as the insert/edit image button text.
|
||||
Fixed so paste as plain text produces BR:s in PRE block and when forced_root_block is disabled.
|
||||
Version 4.0 (2013-06-13)
|
||||
Added new insertdate_dateformat, insertdate_timeformat and insertdate_formats options to insertdatetime.
|
||||
Added new font_formats, fontsize_formats and block_formats options to configure fontselect, fontsizeselect and formatselect.
|
||||
Added new table_clone_elements option to table plugin. Enables you to specify what elements to clone when adding columns/rows.
|
||||
Added new auto detect logic for site and email urls in link plugin to match the logic found in 3.x.
|
||||
Added new getParams/setParams to WindowManager to make it easier to handle params to iframe based dialogs. Contributed by Ryan Demmer.
|
||||
Added new textcolor options that enables you to specify the colors you want to display. Contributed by Jennifer Arsenault.
|
||||
Added new external file support for link_list and image_list options. The file format is a simple JSON file.
|
||||
Added new "both" mode for the resize option. Enables resizing in both width and height.
|
||||
Added new paste_data_images option that allows you to enable/disable paste of data images.
|
||||
Added new fixed_toolbar_container option that allows you to add a fixed container for the inline toolbar.
|
||||
Fixed so font name, font size and block format select boxes gets updated with the current format.
|
||||
Fixed so the resizeTo/resizeBy methods for the theme are exposed as it as in 3.x.
|
||||
Fixed so the textcolor controls are splitbuttons as in 3.x. Patch contributed by toxalot/jashua212.
|
||||
Fixed bug where the theme content css wasn't loaded into the preview dialog.
|
||||
Fixed bug where the template description in template dialog wouldn't display the text correctly.
|
||||
Fixed bug where various UI elements wasn't properly removed when an editor instance was removed.
|
||||
Fixed bug where editing links in inline mode would fail on WebKit.
|
||||
Fixed bug where the pagebreak_separator option in the pagebreak plugin wasn't working properly.
|
||||
Fixed bug where the child panels of the float panel in inline mode wasn't properly placed.
|
||||
Fixed bug where the float panel children of windows wasn't position fixed.
|
||||
Fixed bug where the size of the ok button was hardcoded, caused issues with i18n.
|
||||
Fixed bug where single comment in editor would cause exceptions due to resolve path logic not detecting elements only.
|
||||
Fixed bug where switching alignment of tables in dialogs wouldn't properly remove existing alignments.
|
||||
Fixed bug where the table properties dialog would show columns/rows textboxes.
|
||||
Fixed bug where jQuery wasn't used instead of Sizzle in the jQuery version of TinyMCE.
|
||||
Fixed bug where setting resize option to false whouldn't properly render the word count.
|
||||
Fixed bug where table row type change would produce multiple table section elements.
|
||||
Fixed bug where table row type change on multiple rows would add them in incorrect order.
|
||||
Fixed bug where fullscreen plugin would maximize the editor on resize after toggling it off.
|
||||
Fixed bug where context menu would be position at an incorrect coordinate in inline mode.
|
||||
Fixed bug where inserting lists in inline mode on IE would produce errors since the body would be converted.
|
||||
Fixed bug where the body couldn't be styled properly in custom content_css files.
|
||||
Fixed bug where template plugins menu item would override the image menu item.
|
||||
Fixed bug where IE 7-8 would render the text inside inputs at the wrong vertical location.
|
||||
Fixed bug where IE configured to IE 7 compatibility mode wouldn't render the icons properly.
|
||||
Fixed bug where editor.focus wouldn't properly fire the focusin event on WebKit.
|
||||
Fixed bug where some keyboard shortcuts wouldn't work on IE 8.
|
||||
Fixed bug where the undo state wasn't updated until the end of a typing level.
|
||||
Fixed bug where keyboard shortcuts on Mac OS wasn't working correctly.
|
||||
Fixed bug where empty inline elements would be created when toggling formatting of in empty block.
|
||||
Fixed bug where applying styles on WebKit would fail in inline mode if the user released the mouse button outside the body.
|
||||
Fixed bug where the visual aids menu item wasn't selected if the editor was empty.
|
||||
Fixed so the isDirty/isNotDirty states gets updated to true/false on save() and change events.
|
||||
Fixed so skins have separate CSS files for inline and iframe mode.
|
||||
Fixed so menus and tool tips gets constrained to the current viewport.
|
||||
Fixed so an error is thrown if users load jQuery after the jQuery version of TinyMCE.
|
||||
Fixed so the filetype for media dialog passes out media instead of image as file type.
|
||||
Fixed so it's possible to disable the toolbar by setting it to false.
|
||||
Fixed so autoresize plugin isn't initialized when the editor is in inline mode.
|
||||
Fixed so the inline editing toolbar will be rendered below elements if it doesn't fit above it.
|
||||
Version 4.0b3 (2013-05-15)
|
||||
Added new optional advanced tab for image dialog with hspace, vspace, border and style.
|
||||
Added new change event that gets fired when undo levels are added to editor instances.
|
||||
Added new removed_menuitems option enables you to list menu items to remove from menus.
|
||||
Added new external_plugins option enables you to specify external locations for plugins.
|
||||
Added new language_url option enables you to specify an external location for the language pack.
|
||||
Added new table toolbar control that displays a menu for inserting/editing menus.
|
||||
Fixed bug where IE 10 wouldn't load files properly from cache.
|
||||
Fixed bug where image dialog wouldn't properly remove width/height if blanked.
|
||||
Fixed bug where all events wasn't properly unbound when editor instances where removed.
|
||||
Fixed bug where data- attributes wasn't working properly in the SaxParser.
|
||||
Fixed bug where Gecko wouldn't properly render broken images.
|
||||
Fixed bug where Gecko wouldn't produce the same error dialog on paste as other browsers.
|
||||
Fixed bug where is wasn't possible to prevent execCommands in beforeExecCommand event.
|
||||
Fixed bug where the fullpage_hide_in_source_view option wasn't working in the fullpage plugin.
|
||||
Fixed bug where the WindowManager close method wouldn't properly close the top most window.
|
||||
Fixed bug where it wasn't possible to paste in IE 10 due to JS exception.
|
||||
Fixed bug where tab key didn't move to the right child control in tabpanels.
|
||||
Fixed bug where enter inside a form would focus the first button like control in TinyMCE.
|
||||
Fixed bug where it would match scripts that looked like the tinymce base directory incorrectly.
|
||||
Fixed bug where the spellchecker wouldn't properly toggle off the spellcheck mode if no errors where found.
|
||||
Fixed bug in searchreplace plugin where it would remove all spans instead of the marker spans.
|
||||
Fixed issue where selector wouldn't disable existing mode setting.
|
||||
Fixed so it's easier to configure the menu and menubar.
|
||||
Fixed so bodyId/bodyClass is applied to preview as it's done to the editor iframe.
|
||||
Version 4.0b2 (2013-04-24)
|
||||
Added new rel_list option to link plugin. Enables you to specify values for a rel drop down.
|
||||
Added new target_list option to link plugin. Enables you to add to or disable the link targets.
|
||||
Added new link_list option to link plugin. Enables you to specify a list of links to pick from.
|
||||
Added new image_list option to image pluigin. Enables you to specify a list of images to pick from.
|
||||
Added new textcolor plugin. This plugin holds the text color and text background color buttons.
|
||||
Fixed bug where alignment of images wasn't working properly on Firefox.
|
||||
Fixed bug where IE 8 would throw error when inserting a table.
|
||||
Fixed bug where IE 8 wouldn't render the element path properly.
|
||||
Fixed bug where old IE versions would render a red focus border.
|
||||
Fixed bug where old IE versions would render a frameborder for iframes.
|
||||
Fixed bug where WebKit wouldn't properly open the cell properties dialog on edge case selection.
|
||||
Fixed bug where charmap wouldn't correctly render all characters in grid.
|
||||
Fixed bug where link dialog wouldn't update the link text properly.
|
||||
Fixed bug where the focus/blur states on inline editors wasn't handled correctly on IE.
|
||||
Fixed bug where IE would throw "unknown error" exception sometimes in ForceBlocks logic.
|
||||
Fixed bug where IE would't properly render disabled buttons in button groups.
|
||||
Fixed bug where tab key wouldn't properly move to next input field in dialogs.
|
||||
Fixed bug where resize handles for tables and images would appear at wrong positions on IE 8.
|
||||
Fixed bug where dialogs would produce stack overflow if title was wider than content.
|
||||
Fixed bug with table cell/row menu items being enabled even if no cell was selected.
|
||||
Fixed so the text to display is after the URL field in the link dialog.
|
||||
Fixed so the width setting applies to the editor panel in modern theme.
|
||||
Fixed so it's easier to make custom icons for buttons using plain old images.
|
||||
Version 4.0b1 (2013-04-11)
|
||||
Added new node.js based build process used uglify, amdlc, jake etc.
|
||||
Added new package.json to enable easy installation of dependent npm packages used for building.
|
||||
Added new link, image, charmap, anchor, code, hr plugins since these are now moved out of the theme.
|
||||
Rewrote all plugins and themes from scratch so they match the new UI framework.
|
||||
Replaced all events to use the more common <target>.on/off(<event>) methods instead of <target>.<event>.add/remove.
|
||||
Rewrote the TinyMCE core to use AMD style modules. Gets compiled to an inline library using amdlc.
|
||||
Rewrote all core logic to pass jshint rules. Each file has specific jshint rules.
|
||||
Removed all IE6 specific logic since 4.x will no longer support such an old browser.
|
||||
Reworked the file names and directory structure of the whole project to be more similar to other JS projects.
|
||||
Replaced tinymce.util.Cookie with tinymce.util.LocalStorage. Fallback to userData for IE 7 native localStorage for the rest.
|
||||
Replaced the old 3.x UI with a new modern UI framework.
|
||||
Removed "simple" theme and added new "modern" theme.
|
||||
Removed advhr, advimage, advlink, iespell, inlinepopups, xhtmlxtras and style plugins.
|
||||
Updated Sizzle to the latest version.
|
|
@ -0,0 +1 @@
|
|||
!function(a){function b(){function b(a){"remove"===a&&this.each(function(a,b){var c=e(b);c&&c.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(a,b){var c=tinymce.get(b.id.replace(/_parent$/,""));c&&c.remove()})}function d(a){var c,d=this;if(null!=a)b.call(d),d.each(function(b,c){var d;(d=tinymce.get(c.id))&&d.setContent(a)});else if(d.length>0&&(c=tinymce.get(d[0].id)))return c.getContent()}function e(a){var b=null;return a&&a.id&&f.tinymce&&(b=tinymce.get(a.id)),b}function g(a){return!!(a&&a.length&&f.tinymce&&a.is(":tinymce"))}var h={};a.each(["text","html","val"],function(b,f){var i=h[f]=a.fn[f],j="text"===f;a.fn[f]=function(b){var f=this;if(!g(f))return i.apply(f,arguments);if(b!==c)return d.call(f.filter(":tinymce"),b),i.apply(f.not(":tinymce"),arguments),f;var h="",k=arguments;return(j?f:f.eq(0)).each(function(b,c){var d=e(c);h+=d?j?d.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):d.getContent({save:!0}):i.apply(a(c),k)}),h}}),a.each(["append","prepend"],function(b,d){var f=h[d]=a.fn[d],i="prepend"===d;a.fn[d]=function(a){var b=this;return g(b)?a!==c?("string"==typeof a&&b.filter(":tinymce").each(function(b,c){var d=e(c);d&&d.setContent(i?a+d.getContent():d.getContent()+a)}),f.apply(b.not(":tinymce"),arguments),b):void 0:f.apply(b,arguments)}}),a.each(["remove","replaceWith","replaceAll","empty"],function(c,d){var e=h[d]=a.fn[d];a.fn[d]=function(){return b.call(this,d),e.apply(this,arguments)}}),h.attr=a.fn.attr,a.fn.attr=function(b,f){var i=this,j=arguments;if(!b||"value"!==b||!g(i))return f!==c?h.attr.apply(i,j):h.attr.apply(i,j);if(f!==c)return d.call(i.filter(":tinymce"),f),h.attr.apply(i.not(":tinymce"),j),i;var k=i[0],l=e(k);return l?l.getContent({save:!0}):h.attr.apply(a(k),j)}}var c,d,e=[],f=window;a.fn.tinymce=function(c){function g(){var d=[],e=0;k||(b(),k=!0),l.each(function(a,b){var f,g=b.id,h=c.oninit;g||(b.id=g=tinymce.DOM.uniqueId()),tinymce.get(g)||(f=new tinymce.Editor(g,c,tinymce.EditorManager),d.push(f),f.on("init",function(){var a,b=h;l.css("visibility",""),h&&++e==d.length&&("string"==typeof b&&(a=-1===b.indexOf(".")?null:tinymce.resolve(b.replace(/\.\w+$/,"")),b=tinymce.resolve(b)),b.apply(a||tinymce,d))}))}),a.each(d,function(a,b){b.render()})}var h,i,j,k,l=this,m="";if(!l.length)return l;if(!c)return window.tinymce?tinymce.get(l[0].id):null;if(l.css("visibility","hidden"),f.tinymce||d||!(h=c.script_url))1===d?e.push(g):g();else{d=1,i=h.substring(0,h.lastIndexOf("/")),-1!=h.indexOf(".min")&&(m=".min"),f.tinymce=f.tinyMCEPreInit||{base:i,suffix:m},-1!=h.indexOf("gzip")&&(j=c.language||"en",h=h+(/\?/.test(h)?"&":"?")+"js=true&core=true&suffix="+escape(m)+"&themes="+escape(c.theme||"modern")+"&plugins="+escape(c.plugins||"")+"&languages="+(j||""),f.tinyMCE_GZ||(f.tinyMCE_GZ={start:function(){function b(a){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(a))}b("langs/"+j+".js"),b("themes/"+c.theme+"/theme"+m+".js"),b("themes/"+c.theme+"/langs/"+j+".js"),a.each(c.plugins.split(","),function(a,c){c&&(b("plugins/"+c+"/plugin"+m+".js"),b("plugins/"+c+"/langs/"+j+".js"))})},end:function(){}}));var n=document.createElement("script");n.type="text/javascript",n.onload=n.onreadystatechange=function(b){b=b||window.event,2===d||"load"!=b.type&&!/complete|loaded/.test(n.readyState)||(tinymce.dom.Event.domLoaded=1,d=2,c.script_loaded&&c.script_loaded(),g(),a.each(e,function(a,b){b()}))},n.src=h,document.body.appendChild(n)}return l},a.extend(a.expr[":"],{tinymce:function(a){var b;return a.id&&"tinymce"in window&&(b=tinymce.get(a.id),b&&b.editorManager===tinymce)?!0:!1}})}(jQuery);
|
|
@ -0,0 +1,504 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('advlist', function(editor) {
|
||||
var olMenuItems, ulMenuItems, lastStyles = {};
|
||||
|
||||
function buildMenuItems(listName, styleValues) {
|
||||
var items = [];
|
||||
|
||||
tinymce.each(styleValues.split(/[ ,]/), function(styleValue) {
|
||||
items.push({
|
||||
text: styleValue.replace(/\-/g, ' ').replace(/\b\w/g, function(chr) {
|
||||
return chr.toUpperCase();
|
||||
}),
|
||||
data: styleValue == 'default' ? '' : styleValue
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
olMenuItems = buildMenuItems('OL', editor.getParam(
|
||||
"advlist_number_styles",
|
||||
"default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman"
|
||||
));
|
||||
|
||||
ulMenuItems = buildMenuItems('UL', editor.getParam("advlist_bullet_styles", "default,circle,disc,square"));
|
||||
|
||||
function applyListFormat(listName, styleValue) {
|
||||
editor.undoManager.transact(function() {
|
||||
var list, dom = editor.dom, sel = editor.selection;
|
||||
|
||||
// Check for existing list element
|
||||
list = dom.getParent(sel.getNode(), 'ol,ul');
|
||||
|
||||
// Switch/add list type if needed
|
||||
if (!list || list.nodeName != listName || styleValue === false) {
|
||||
editor.execCommand(listName == 'UL' ? 'InsertUnorderedList' : 'InsertOrderedList');
|
||||
}
|
||||
|
||||
// Set style
|
||||
styleValue = styleValue === false ? lastStyles[listName] : styleValue;
|
||||
lastStyles[listName] = styleValue;
|
||||
|
||||
list = dom.getParent(sel.getNode(), 'ol,ul');
|
||||
if (list) {
|
||||
dom.setStyle(list, 'listStyleType', styleValue ? styleValue : null);
|
||||
list.removeAttribute('data-mce-style');
|
||||
}
|
||||
|
||||
editor.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelection(e) {
|
||||
var listStyleType = editor.dom.getStyle(editor.dom.getParent(editor.selection.getNode(), 'ol,ul'), 'listStyleType') || '';
|
||||
|
||||
e.control.items().each(function(ctrl) {
|
||||
ctrl.active(ctrl.settings.data === listStyleType);
|
||||
});
|
||||
}
|
||||
|
||||
editor.addButton('numlist', {
|
||||
type: 'splitbutton',
|
||||
tooltip: 'Numbered list',
|
||||
menu: olMenuItems,
|
||||
onshow: updateSelection,
|
||||
onselect: function(e) {
|
||||
applyListFormat('OL', e.control.settings.data);
|
||||
},
|
||||
onclick: function() {
|
||||
applyListFormat('OL', false);
|
||||
}
|
||||
});
|
||||
|
||||
editor.addButton('bullist', {
|
||||
type: 'splitbutton',
|
||||
tooltip: 'Bullet list',
|
||||
menu: ulMenuItems,
|
||||
onshow: updateSelection,
|
||||
onselect: function(e) {
|
||||
applyListFormat('UL', e.control.settings.data);
|
||||
},
|
||||
onclick: function() {
|
||||
applyListFormat('UL', false);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("advlist",function(a){function b(a,b){var c=[];return tinymce.each(b.split(/[ ,]/),function(a){c.push({text:a.replace(/\-/g," ").replace(/\b\w/g,function(a){return a.toUpperCase()}),data:"default"==a?"":a})}),c}function c(b,c){a.undoManager.transact(function(){var d,e=a.dom,f=a.selection;d=e.getParent(f.getNode(),"ol,ul"),d&&d.nodeName==b&&c!==!1||a.execCommand("UL"==b?"InsertUnorderedList":"InsertOrderedList"),c=c===!1?g[b]:c,g[b]=c,d=e.getParent(f.getNode(),"ol,ul"),d&&(e.setStyle(d,"listStyleType",c?c:null),d.removeAttribute("data-mce-style")),a.focus()})}function d(b){var c=a.dom.getStyle(a.dom.getParent(a.selection.getNode(),"ol,ul"),"listStyleType")||"";b.control.items().each(function(a){a.active(a.settings.data===c)})}var e,f,g={};e=b("OL",a.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman")),f=b("UL",a.getParam("advlist_bullet_styles","default,circle,disc,square")),a.addButton("numlist",{type:"splitbutton",tooltip:"Numbered list",menu:e,onshow:d,onselect:function(a){c("OL",a.control.settings.data)},onclick:function(){c("OL",!1)}}),a.addButton("bullist",{type:"splitbutton",tooltip:"Bullet list",menu:f,onshow:d,onselect:function(a){c("UL",a.control.settings.data)},onclick:function(){c("UL",!1)}})});
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('anchor', function(editor) {
|
||||
function showDialog() {
|
||||
var selectedNode = editor.selection.getNode(), name = '';
|
||||
|
||||
if (selectedNode.tagName == 'A') {
|
||||
name = selectedNode.name || selectedNode.id || '';
|
||||
}
|
||||
|
||||
editor.windowManager.open({
|
||||
title: 'Anchor',
|
||||
body: {type: 'textbox', name: 'name', size: 40, label: 'Name', value: name},
|
||||
onsubmit: function(e) {
|
||||
editor.execCommand('mceInsertContent', false, editor.dom.createHTML('a', {
|
||||
id: e.data.name
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editor.addButton('anchor', {
|
||||
icon: 'anchor',
|
||||
tooltip: 'Anchor',
|
||||
onclick: showDialog,
|
||||
stateSelector: 'a:not([href])'
|
||||
});
|
||||
|
||||
editor.addMenuItem('anchor', {
|
||||
icon: 'anchor',
|
||||
text: 'Anchor',
|
||||
context: 'insert',
|
||||
onclick: showDialog
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("anchor",function(a){function b(){var b=a.selection.getNode(),c="";"A"==b.tagName&&(c=b.name||b.id||""),a.windowManager.open({title:"Anchor",body:{type:"textbox",name:"name",size:40,label:"Name",value:c},onsubmit:function(b){a.execCommand("mceInsertContent",!1,a.dom.createHTML("a",{id:b.data.name}))}})}a.addButton("anchor",{icon:"anchor",tooltip:"Anchor",onclick:b,stateSelector:"a:not([href])"}),a.addMenuItem("anchor",{icon:"anchor",text:"Anchor",context:"insert",onclick:b})});
|
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright 2011, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('autolink', function(editor) {
|
||||
var AutoUrlDetectState;
|
||||
|
||||
editor.on("keydown", function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
return handleEnter(editor);
|
||||
}
|
||||
});
|
||||
|
||||
// Internet Explorer has built-in automatic linking for most cases
|
||||
if (tinymce.Env.ie) {
|
||||
editor.on("focus", function() {
|
||||
if (!AutoUrlDetectState) {
|
||||
AutoUrlDetectState = true;
|
||||
|
||||
try {
|
||||
editor.execCommand('AutoUrlDetect', false, true);
|
||||
} catch (ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
editor.on("keypress", function(e) {
|
||||
if (e.keyCode == 41) {
|
||||
return handleEclipse(editor);
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("keyup", function(e) {
|
||||
if (e.keyCode == 32) {
|
||||
return handleSpacebar(editor);
|
||||
}
|
||||
});
|
||||
|
||||
function handleEclipse(editor) {
|
||||
parseCurrentLine(editor, -1, '(', true);
|
||||
}
|
||||
|
||||
function handleSpacebar(editor) {
|
||||
parseCurrentLine(editor, 0, '', true);
|
||||
}
|
||||
|
||||
function handleEnter(editor) {
|
||||
parseCurrentLine(editor, -1, '', false);
|
||||
}
|
||||
|
||||
function parseCurrentLine(editor, end_offset, delimiter) {
|
||||
var rng, end, start, endContainer, bookmark, text, matches, prev, len, rngText;
|
||||
|
||||
function scopeIndex(container, index) {
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if (container.nodeType == 3) {
|
||||
var len = container.data.length;
|
||||
|
||||
if (index > len) {
|
||||
index = len;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
function setStart(container, offset) {
|
||||
if (container.nodeType != 1 || container.hasChildNodes()) {
|
||||
rng.setStart(container, scopeIndex(container, offset));
|
||||
} else {
|
||||
rng.setStartBefore(container);
|
||||
}
|
||||
}
|
||||
|
||||
function setEnd(container, offset) {
|
||||
if (container.nodeType != 1 || container.hasChildNodes()) {
|
||||
rng.setEnd(container, scopeIndex(container, offset));
|
||||
} else {
|
||||
rng.setEndAfter(container);
|
||||
}
|
||||
}
|
||||
|
||||
// We need at least five characters to form a URL,
|
||||
// hence, at minimum, five characters from the beginning of the line.
|
||||
rng = editor.selection.getRng(true).cloneRange();
|
||||
if (rng.startOffset < 5) {
|
||||
// During testing, the caret is placed inbetween two text nodes.
|
||||
// The previous text node contains the URL.
|
||||
prev = rng.endContainer.previousSibling;
|
||||
if (!prev) {
|
||||
if (!rng.endContainer.firstChild || !rng.endContainer.firstChild.nextSibling) {
|
||||
return;
|
||||
}
|
||||
|
||||
prev = rng.endContainer.firstChild.nextSibling;
|
||||
}
|
||||
|
||||
len = prev.length;
|
||||
setStart(prev, len);
|
||||
setEnd(prev, len);
|
||||
|
||||
if (rng.endOffset < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
end = rng.endOffset;
|
||||
endContainer = prev;
|
||||
} else {
|
||||
endContainer = rng.endContainer;
|
||||
|
||||
// Get a text node
|
||||
if (endContainer.nodeType != 3 && endContainer.firstChild) {
|
||||
while (endContainer.nodeType != 3 && endContainer.firstChild) {
|
||||
endContainer = endContainer.firstChild;
|
||||
}
|
||||
|
||||
// Move range to text node
|
||||
if (endContainer.nodeType == 3) {
|
||||
setStart(endContainer, 0);
|
||||
setEnd(endContainer, endContainer.nodeValue.length);
|
||||
}
|
||||
}
|
||||
|
||||
if (rng.endOffset == 1) {
|
||||
end = 2;
|
||||
} else {
|
||||
end = rng.endOffset - 1 - end_offset;
|
||||
}
|
||||
}
|
||||
|
||||
start = end;
|
||||
|
||||
do {
|
||||
// Move the selection one character backwards.
|
||||
setStart(endContainer, end >= 2 ? end - 2 : 0);
|
||||
setEnd(endContainer, end >= 1 ? end - 1 : 0);
|
||||
end -= 1;
|
||||
rngText = rng.toString();
|
||||
|
||||
// Loop until one of the following is found: a blank space, , delimiter, (end-2) >= 0
|
||||
} while (rngText != ' ' && rngText !== '' && rngText.charCodeAt(0) != 160 && (end - 2) >= 0 && rngText != delimiter);
|
||||
|
||||
if (rng.toString() == delimiter || rng.toString().charCodeAt(0) == 160) {
|
||||
setStart(endContainer, end);
|
||||
setEnd(endContainer, start);
|
||||
end += 1;
|
||||
} else if (rng.startOffset === 0) {
|
||||
setStart(endContainer, 0);
|
||||
setEnd(endContainer, start);
|
||||
} else {
|
||||
setStart(endContainer, end);
|
||||
setEnd(endContainer, start);
|
||||
}
|
||||
|
||||
// Exclude last . from word like "www.site.com."
|
||||
text = rng.toString();
|
||||
if (text.charAt(text.length - 1) == '.') {
|
||||
setEnd(endContainer, start - 1);
|
||||
}
|
||||
|
||||
text = rng.toString();
|
||||
matches = text.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i);
|
||||
|
||||
if (matches) {
|
||||
if (matches[1] == 'www.') {
|
||||
matches[1] = 'http://www.';
|
||||
} else if (/@$/.test(matches[1]) && !/^mailto:/.test(matches[1])) {
|
||||
matches[1] = 'mailto:' + matches[1];
|
||||
}
|
||||
|
||||
bookmark = editor.selection.getBookmark();
|
||||
|
||||
editor.selection.setRng(rng);
|
||||
editor.execCommand('createlink', false, matches[1] + matches[2]);
|
||||
editor.selection.moveToBookmark(bookmark);
|
||||
editor.nodeChanged();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("autolink",function(a){function b(a){e(a,-1,"(",!0)}function c(a){e(a,0,"",!0)}function d(a){e(a,-1,"",!1)}function e(a,b,c){function d(a,b){if(0>b&&(b=0),3==a.nodeType){var c=a.data.length;b>c&&(b=c)}return b}function e(a,b){1!=a.nodeType||a.hasChildNodes()?g.setStart(a,d(a,b)):g.setStartBefore(a)}function f(a,b){1!=a.nodeType||a.hasChildNodes()?g.setEnd(a,d(a,b)):g.setEndAfter(a)}var g,h,i,j,k,l,m,n,o,p;if(g=a.selection.getRng(!0).cloneRange(),g.startOffset<5){if(n=g.endContainer.previousSibling,!n){if(!g.endContainer.firstChild||!g.endContainer.firstChild.nextSibling)return;n=g.endContainer.firstChild.nextSibling}if(o=n.length,e(n,o),f(n,o),g.endOffset<5)return;h=g.endOffset,j=n}else{if(j=g.endContainer,3!=j.nodeType&&j.firstChild){for(;3!=j.nodeType&&j.firstChild;)j=j.firstChild;3==j.nodeType&&(e(j,0),f(j,j.nodeValue.length))}h=1==g.endOffset?2:g.endOffset-1-b}i=h;do e(j,h>=2?h-2:0),f(j,h>=1?h-1:0),h-=1,p=g.toString();while(" "!=p&&""!==p&&160!=p.charCodeAt(0)&&h-2>=0&&p!=c);g.toString()==c||160==g.toString().charCodeAt(0)?(e(j,h),f(j,i),h+=1):0===g.startOffset?(e(j,0),f(j,i)):(e(j,h),f(j,i)),l=g.toString(),"."==l.charAt(l.length-1)&&f(j,i-1),l=g.toString(),m=l.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+\-]+@)(.+)$/i),m&&("www."==m[1]?m[1]="http://www.":/@$/.test(m[1])&&!/^mailto:/.test(m[1])&&(m[1]="mailto:"+m[1]),k=a.selection.getBookmark(),a.selection.setRng(g),a.execCommand("createlink",!1,m[1]+m[2]),a.selection.moveToBookmark(k),a.nodeChanged())}var f;return a.on("keydown",function(b){return 13==b.keyCode?d(a):void 0}),tinymce.Env.ie?void a.on("focus",function(){if(!f){f=!0;try{a.execCommand("AutoUrlDetect",!1,!0)}catch(b){}}}):(a.on("keypress",function(c){return 41==c.keyCode?b(a):void 0}),void a.on("keyup",function(b){return 32==b.keyCode?c(a):void 0}))});
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
/*eslint no-nested-ternary:0 */
|
||||
|
||||
/**
|
||||
* Auto Resize
|
||||
*
|
||||
* This plugin automatically resizes the content area to fit its content height.
|
||||
* It will retain a minimum height, which is the height of the content area when
|
||||
* it's initialized.
|
||||
*/
|
||||
tinymce.PluginManager.add('autoresize', function(editor) {
|
||||
var settings = editor.settings, oldSize = 0;
|
||||
|
||||
function isFullscreen() {
|
||||
return editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
|
||||
}
|
||||
|
||||
if (editor.settings.inline) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets executed each time the editor needs to resize.
|
||||
*/
|
||||
function resize(e) {
|
||||
var deltaSize, doc, body, docElm, DOM = tinymce.DOM, resizeHeight, myHeight,
|
||||
marginTop, marginBottom, paddingTop, paddingBottom, borderTop, borderBottom;
|
||||
|
||||
doc = editor.getDoc();
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
body = doc.body;
|
||||
docElm = doc.documentElement;
|
||||
resizeHeight = settings.autoresize_min_height;
|
||||
|
||||
if (!body || (e && e.type === "setcontent" && e.initial) || isFullscreen()) {
|
||||
if (body && docElm) {
|
||||
body.style.overflowY = "auto";
|
||||
docElm.style.overflowY = "auto"; // Old IE
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate outer height of the body element using CSS styles
|
||||
marginTop = editor.dom.getStyle(body, 'margin-top', true);
|
||||
marginBottom = editor.dom.getStyle(body, 'margin-bottom', true);
|
||||
paddingTop = editor.dom.getStyle(body, 'padding-top', true);
|
||||
paddingBottom = editor.dom.getStyle(body, 'padding-bottom', true);
|
||||
borderTop = editor.dom.getStyle(body, 'border-top-width', true);
|
||||
borderBottom = editor.dom.getStyle(body, 'border-bottom-width', true);
|
||||
myHeight = body.offsetHeight + parseInt(marginTop, 10) + parseInt(marginBottom, 10) +
|
||||
parseInt(paddingTop, 10) + parseInt(paddingBottom, 10) +
|
||||
parseInt(borderTop, 10) + parseInt(borderBottom, 10);
|
||||
|
||||
// Make sure we have a valid height
|
||||
if (isNaN(myHeight) || myHeight <= 0) {
|
||||
// Get height differently depending on the browser used
|
||||
myHeight = tinymce.Env.ie ? body.scrollHeight : (tinymce.Env.webkit && body.clientHeight === 0 ? 0 : body.offsetHeight);
|
||||
}
|
||||
|
||||
// Don't make it smaller than the minimum height
|
||||
if (myHeight > settings.autoresize_min_height) {
|
||||
resizeHeight = myHeight;
|
||||
}
|
||||
|
||||
// If a maximum height has been defined don't exceed this height
|
||||
if (settings.autoresize_max_height && myHeight > settings.autoresize_max_height) {
|
||||
resizeHeight = settings.autoresize_max_height;
|
||||
body.style.overflowY = "auto";
|
||||
docElm.style.overflowY = "auto"; // Old IE
|
||||
} else {
|
||||
body.style.overflowY = "hidden";
|
||||
docElm.style.overflowY = "hidden"; // Old IE
|
||||
body.scrollTop = 0;
|
||||
}
|
||||
|
||||
// Resize content element
|
||||
if (resizeHeight !== oldSize) {
|
||||
deltaSize = resizeHeight - oldSize;
|
||||
DOM.setStyle(editor.iframeElement, 'height', resizeHeight + 'px');
|
||||
oldSize = resizeHeight;
|
||||
|
||||
// WebKit doesn't decrease the size of the body element until the iframe gets resized
|
||||
// So we need to continue to resize the iframe down until the size gets fixed
|
||||
if (tinymce.isWebKit && deltaSize < 0) {
|
||||
resize(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the resize x times in 100ms intervals. We can't wait for load events since
|
||||
* the CSS files might load async.
|
||||
*/
|
||||
function wait(times, interval, callback) {
|
||||
setTimeout(function() {
|
||||
resize({});
|
||||
|
||||
if (times--) {
|
||||
wait(times, interval, callback);
|
||||
} else if (callback) {
|
||||
callback();
|
||||
}
|
||||
}, interval);
|
||||
}
|
||||
|
||||
// Define minimum height
|
||||
settings.autoresize_min_height = parseInt(editor.getParam('autoresize_min_height', editor.getElement().offsetHeight), 10);
|
||||
|
||||
// Define maximum height
|
||||
settings.autoresize_max_height = parseInt(editor.getParam('autoresize_max_height', 0), 10);
|
||||
|
||||
// Add padding at the bottom for better UX
|
||||
editor.on("init", function() {
|
||||
var overflowPadding = editor.getParam('autoresize_overflow_padding', 1);
|
||||
|
||||
editor.dom.setStyles(editor.getBody(), {
|
||||
paddingBottom: editor.getParam('autoresize_bottom_margin', 50),
|
||||
paddingLeft: overflowPadding,
|
||||
paddingRight: overflowPadding
|
||||
});
|
||||
});
|
||||
|
||||
// Add appropriate listeners for resizing content area
|
||||
editor.on("nodechange setcontent keyup FullscreenStateChanged", resize);
|
||||
|
||||
if (editor.getParam('autoresize_on_init', true)) {
|
||||
editor.on('init', function() {
|
||||
// Hit it 20 times in 100 ms intervals
|
||||
wait(20, 100, function() {
|
||||
// Hit it 5 times in 1 sec intervals
|
||||
wait(5, 1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');
|
||||
editor.addCommand('mceAutoResize', resize);
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("autoresize",function(a){function b(){return a.plugins.fullscreen&&a.plugins.fullscreen.isFullscreen()}function c(d){var g,h,i,j,k,l,m,n,o,p,q,r,s=tinymce.DOM;if(h=a.getDoc()){if(i=h.body,j=h.documentElement,k=e.autoresize_min_height,!i||d&&"setcontent"===d.type&&d.initial||b())return void(i&&j&&(i.style.overflowY="auto",j.style.overflowY="auto"));m=a.dom.getStyle(i,"margin-top",!0),n=a.dom.getStyle(i,"margin-bottom",!0),o=a.dom.getStyle(i,"padding-top",!0),p=a.dom.getStyle(i,"padding-bottom",!0),q=a.dom.getStyle(i,"border-top-width",!0),r=a.dom.getStyle(i,"border-bottom-width",!0),l=i.offsetHeight+parseInt(m,10)+parseInt(n,10)+parseInt(o,10)+parseInt(p,10)+parseInt(q,10)+parseInt(r,10),(isNaN(l)||0>=l)&&(l=tinymce.Env.ie?i.scrollHeight:tinymce.Env.webkit&&0===i.clientHeight?0:i.offsetHeight),l>e.autoresize_min_height&&(k=l),e.autoresize_max_height&&l>e.autoresize_max_height?(k=e.autoresize_max_height,i.style.overflowY="auto",j.style.overflowY="auto"):(i.style.overflowY="hidden",j.style.overflowY="hidden",i.scrollTop=0),k!==f&&(g=k-f,s.setStyle(a.iframeElement,"height",k+"px"),f=k,tinymce.isWebKit&&0>g&&c(d))}}function d(a,b,e){setTimeout(function(){c({}),a--?d(a,b,e):e&&e()},b)}var e=a.settings,f=0;a.settings.inline||(e.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight),10),e.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0),10),a.on("init",function(){var b=a.getParam("autoresize_overflow_padding",1);a.dom.setStyles(a.getBody(),{paddingBottom:a.getParam("autoresize_bottom_margin",50),paddingLeft:b,paddingRight:b})}),a.on("nodechange setcontent keyup FullscreenStateChanged",c),a.getParam("autoresize_on_init",!0)&&a.on("init",function(){d(20,100,function(){d(5,1e3)})}),a.addCommand("mceAutoResize",c))});
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
// Internal unload handler will be called before the page is unloaded
|
||||
// Needs to be outside the plugin since it would otherwise keep
|
||||
// a reference to editor in closue scope
|
||||
/*eslint no-func-assign:0 */
|
||||
tinymce._beforeUnloadHandler = function() {
|
||||
var msg;
|
||||
|
||||
tinymce.each(tinymce.editors, function(editor) {
|
||||
// Store a draft for each editor instance
|
||||
if (editor.plugins.autosave) {
|
||||
editor.plugins.autosave.storeDraft();
|
||||
}
|
||||
|
||||
// Setup a return message if the editor is dirty
|
||||
if (!msg && editor.isDirty() && editor.getParam("autosave_ask_before_unload", true)) {
|
||||
msg = editor.translate("You have unsaved changes are you sure you want to navigate away?");
|
||||
}
|
||||
});
|
||||
|
||||
return msg;
|
||||
};
|
||||
|
||||
tinymce.PluginManager.add('autosave', function(editor) {
|
||||
var settings = editor.settings, LocalStorage = tinymce.util.LocalStorage, prefix, started;
|
||||
|
||||
prefix = settings.autosave_prefix || 'tinymce-autosave-{path}{query}-{id}-';
|
||||
prefix = prefix.replace(/\{path\}/g, document.location.pathname);
|
||||
prefix = prefix.replace(/\{query\}/g, document.location.search);
|
||||
prefix = prefix.replace(/\{id\}/g, editor.id);
|
||||
|
||||
function parseTime(time, defaultTime) {
|
||||
var multipels = {
|
||||
s: 1000,
|
||||
m: 60000
|
||||
};
|
||||
|
||||
time = /^(\d+)([ms]?)$/.exec('' + (time || defaultTime));
|
||||
|
||||
return (time[2] ? multipels[time[2]] : 1) * parseInt(time, 10);
|
||||
}
|
||||
|
||||
function hasDraft() {
|
||||
var time = parseInt(LocalStorage.getItem(prefix + "time"), 10) || 0;
|
||||
|
||||
if (new Date().getTime() - time > settings.autosave_retention) {
|
||||
removeDraft(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeDraft(fire) {
|
||||
LocalStorage.removeItem(prefix + "draft");
|
||||
LocalStorage.removeItem(prefix + "time");
|
||||
|
||||
if (fire !== false) {
|
||||
editor.fire('RemoveDraft');
|
||||
}
|
||||
}
|
||||
|
||||
function storeDraft() {
|
||||
if (!isEmpty() && editor.isDirty()) {
|
||||
LocalStorage.setItem(prefix + "draft", editor.getContent({format: 'raw', no_events: true}));
|
||||
LocalStorage.setItem(prefix + "time", new Date().getTime());
|
||||
editor.fire('StoreDraft');
|
||||
}
|
||||
}
|
||||
|
||||
function restoreDraft() {
|
||||
if (hasDraft()) {
|
||||
editor.setContent(LocalStorage.getItem(prefix + "draft"), {format: 'raw'});
|
||||
editor.fire('RestoreDraft');
|
||||
}
|
||||
}
|
||||
|
||||
function startStoreDraft() {
|
||||
if (!started) {
|
||||
setInterval(function() {
|
||||
if (!editor.removed) {
|
||||
storeDraft();
|
||||
}
|
||||
}, settings.autosave_interval);
|
||||
|
||||
started = true;
|
||||
}
|
||||
}
|
||||
|
||||
settings.autosave_interval = parseTime(settings.autosave_interval, '30s');
|
||||
settings.autosave_retention = parseTime(settings.autosave_retention, '20m');
|
||||
|
||||
function postRender() {
|
||||
var self = this;
|
||||
|
||||
self.disabled(!hasDraft());
|
||||
|
||||
editor.on('StoreDraft RestoreDraft RemoveDraft', function() {
|
||||
self.disabled(!hasDraft());
|
||||
});
|
||||
|
||||
startStoreDraft();
|
||||
}
|
||||
|
||||
function restoreLastDraft() {
|
||||
editor.undoManager.beforeChange();
|
||||
restoreDraft();
|
||||
removeDraft();
|
||||
editor.undoManager.add();
|
||||
}
|
||||
|
||||
editor.addButton('restoredraft', {
|
||||
title: 'Restore last draft',
|
||||
onclick: restoreLastDraft,
|
||||
onPostRender: postRender
|
||||
});
|
||||
|
||||
editor.addMenuItem('restoredraft', {
|
||||
text: 'Restore last draft',
|
||||
onclick: restoreLastDraft,
|
||||
onPostRender: postRender,
|
||||
context: 'file'
|
||||
});
|
||||
|
||||
function isEmpty(html) {
|
||||
var forcedRootBlockName = editor.settings.forced_root_block;
|
||||
|
||||
html = tinymce.trim(typeof(html) == "undefined" ? editor.getBody().innerHTML : html);
|
||||
|
||||
return html === '' || new RegExp(
|
||||
'^<' + forcedRootBlockName + '[^>]*>((\u00a0| |[ \t]|<br[^>]*>)+?|)<\/' + forcedRootBlockName + '>|<br>$', 'i'
|
||||
).test(html);
|
||||
}
|
||||
|
||||
if (editor.settings.autosave_restore_when_empty !== false) {
|
||||
editor.on('init', function() {
|
||||
if (hasDraft() && isEmpty()) {
|
||||
restoreDraft();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('saveContent', function() {
|
||||
removeDraft();
|
||||
});
|
||||
}
|
||||
|
||||
window.onbeforeunload = tinymce._beforeUnloadHandler;
|
||||
|
||||
this.hasDraft = hasDraft;
|
||||
this.storeDraft = storeDraft;
|
||||
this.restoreDraft = restoreDraft;
|
||||
this.removeDraft = removeDraft;
|
||||
this.isEmpty = isEmpty;
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce._beforeUnloadHandler=function(){var a;return tinymce.each(tinymce.editors,function(b){b.plugins.autosave&&b.plugins.autosave.storeDraft(),!a&&b.isDirty()&&b.getParam("autosave_ask_before_unload",!0)&&(a=b.translate("You have unsaved changes are you sure you want to navigate away?"))}),a},tinymce.PluginManager.add("autosave",function(a){function b(a,b){var c={s:1e3,m:6e4};return a=/^(\d+)([ms]?)$/.exec(""+(a||b)),(a[2]?c[a[2]]:1)*parseInt(a,10)}function c(){var a=parseInt(n.getItem(k+"time"),10)||0;return(new Date).getTime()-a>m.autosave_retention?(d(!1),!1):!0}function d(b){n.removeItem(k+"draft"),n.removeItem(k+"time"),b!==!1&&a.fire("RemoveDraft")}function e(){!j()&&a.isDirty()&&(n.setItem(k+"draft",a.getContent({format:"raw",no_events:!0})),n.setItem(k+"time",(new Date).getTime()),a.fire("StoreDraft"))}function f(){c()&&(a.setContent(n.getItem(k+"draft"),{format:"raw"}),a.fire("RestoreDraft"))}function g(){l||(setInterval(function(){a.removed||e()},m.autosave_interval),l=!0)}function h(){var b=this;b.disabled(!c()),a.on("StoreDraft RestoreDraft RemoveDraft",function(){b.disabled(!c())}),g()}function i(){a.undoManager.beforeChange(),f(),d(),a.undoManager.add()}function j(b){var c=a.settings.forced_root_block;return b=tinymce.trim("undefined"==typeof b?a.getBody().innerHTML:b),""===b||new RegExp("^<"+c+"[^>]*>((\xa0| |[ ]|<br[^>]*>)+?|)</"+c+">|<br>$","i").test(b)}var k,l,m=a.settings,n=tinymce.util.LocalStorage;k=m.autosave_prefix||"tinymce-autosave-{path}{query}-{id}-",k=k.replace(/\{path\}/g,document.location.pathname),k=k.replace(/\{query\}/g,document.location.search),k=k.replace(/\{id\}/g,a.id),m.autosave_interval=b(m.autosave_interval,"30s"),m.autosave_retention=b(m.autosave_retention,"20m"),a.addButton("restoredraft",{title:"Restore last draft",onclick:i,onPostRender:h}),a.addMenuItem("restoredraft",{text:"Restore last draft",onclick:i,onPostRender:h,context:"file"}),a.settings.autosave_restore_when_empty!==!1&&(a.on("init",function(){c()&&j()&&f()}),a.on("saveContent",function(){d()})),window.onbeforeunload=tinymce._beforeUnloadHandler,this.hasDraft=c,this.storeDraft=e,this.restoreDraft=f,this.removeDraft=d,this.isEmpty=j});
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
(function() {
|
||||
tinymce.create('tinymce.plugins.BBCodePlugin', {
|
||||
init : function(ed) {
|
||||
var self = this, dialect = ed.getParam('bbcode_dialect', 'punbb').toLowerCase();
|
||||
|
||||
ed.on('beforeSetContent', function(e) {
|
||||
e.content = self['_' + dialect + '_bbcode2html'](e.content);
|
||||
});
|
||||
|
||||
ed.on('postProcess', function(e) {
|
||||
if (e.set) {
|
||||
e.content = self['_' + dialect + '_bbcode2html'](e.content);
|
||||
}
|
||||
|
||||
if (e.get) {
|
||||
e.content = self['_' + dialect + '_html2bbcode'](e.content);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getInfo: function() {
|
||||
return {
|
||||
longname: 'BBCode Plugin',
|
||||
author: 'Moxiecode Systems AB',
|
||||
authorurl: 'http://www.tinymce.com',
|
||||
infourl: 'http://www.tinymce.com/wiki.php/Plugin:bbcode'
|
||||
};
|
||||
},
|
||||
|
||||
// Private methods
|
||||
|
||||
// HTML -> BBCode in PunBB dialect
|
||||
_punbb_html2bbcode : function(s) {
|
||||
s = tinymce.trim(s);
|
||||
|
||||
function rep(re, str) {
|
||||
s = s.replace(re, str);
|
||||
}
|
||||
|
||||
// example: <strong> to [b]
|
||||
rep(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi, "[url=$1]$2[/url]");
|
||||
rep(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi, "[code][color=$1]$2[/color][/code]");
|
||||
rep(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi, "[quote][color=$1]$2[/color][/quote]");
|
||||
rep(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi, "[code][color=$1]$2[/color][/code]");
|
||||
rep(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi, "[quote][color=$1]$2[/color][/quote]");
|
||||
rep(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi, "[color=$1]$2[/color]");
|
||||
rep(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi, "[color=$1]$2[/color]");
|
||||
rep(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi, "[size=$1]$2[/size]");
|
||||
rep(/<font>(.*?)<\/font>/gi, "$1");
|
||||
rep(/<img.*?src=\"(.*?)\".*?\/>/gi, "[img]$1[/img]");
|
||||
rep(/<span class=\"codeStyle\">(.*?)<\/span>/gi, "[code]$1[/code]");
|
||||
rep(/<span class=\"quoteStyle\">(.*?)<\/span>/gi, "[quote]$1[/quote]");
|
||||
rep(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi, "[code][b]$1[/b][/code]");
|
||||
rep(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi, "[quote][b]$1[/b][/quote]");
|
||||
rep(/<em class=\"codeStyle\">(.*?)<\/em>/gi, "[code][i]$1[/i][/code]");
|
||||
rep(/<em class=\"quoteStyle\">(.*?)<\/em>/gi, "[quote][i]$1[/i][/quote]");
|
||||
rep(/<u class=\"codeStyle\">(.*?)<\/u>/gi, "[code][u]$1[/u][/code]");
|
||||
rep(/<u class=\"quoteStyle\">(.*?)<\/u>/gi, "[quote][u]$1[/u][/quote]");
|
||||
rep(/<\/(strong|b)>/gi, "[/b]");
|
||||
rep(/<(strong|b)>/gi, "[b]");
|
||||
rep(/<\/(em|i)>/gi, "[/i]");
|
||||
rep(/<(em|i)>/gi, "[i]");
|
||||
rep(/<\/u>/gi, "[/u]");
|
||||
rep(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi, "[u]$1[/u]");
|
||||
rep(/<u>/gi, "[u]");
|
||||
rep(/<blockquote[^>]*>/gi, "[quote]");
|
||||
rep(/<\/blockquote>/gi, "[/quote]");
|
||||
rep(/<br \/>/gi, "\n");
|
||||
rep(/<br\/>/gi, "\n");
|
||||
rep(/<br>/gi, "\n");
|
||||
rep(/<p>/gi, "");
|
||||
rep(/<\/p>/gi, "\n");
|
||||
rep(/ |\u00a0/gi, " ");
|
||||
rep(/"/gi, "\"");
|
||||
rep(/</gi, "<");
|
||||
rep(/>/gi, ">");
|
||||
rep(/&/gi, "&");
|
||||
|
||||
return s;
|
||||
},
|
||||
|
||||
// BBCode -> HTML from PunBB dialect
|
||||
_punbb_bbcode2html : function(s) {
|
||||
s = tinymce.trim(s);
|
||||
|
||||
function rep(re, str) {
|
||||
s = s.replace(re, str);
|
||||
}
|
||||
|
||||
// example: [b] to <strong>
|
||||
rep(/\n/gi, "<br />");
|
||||
rep(/\[b\]/gi, "<strong>");
|
||||
rep(/\[\/b\]/gi, "</strong>");
|
||||
rep(/\[i\]/gi, "<em>");
|
||||
rep(/\[\/i\]/gi, "</em>");
|
||||
rep(/\[u\]/gi, "<u>");
|
||||
rep(/\[\/u\]/gi, "</u>");
|
||||
rep(/\[url=([^\]]+)\](.*?)\[\/url\]/gi, "<a href=\"$1\">$2</a>");
|
||||
rep(/\[url\](.*?)\[\/url\]/gi, "<a href=\"$1\">$1</a>");
|
||||
rep(/\[img\](.*?)\[\/img\]/gi, "<img src=\"$1\" />");
|
||||
rep(/\[color=(.*?)\](.*?)\[\/color\]/gi, "<font color=\"$1\">$2</font>");
|
||||
rep(/\[code\](.*?)\[\/code\]/gi, "<span class=\"codeStyle\">$1</span> ");
|
||||
rep(/\[quote.*?\](.*?)\[\/quote\]/gi, "<span class=\"quoteStyle\">$1</span> ");
|
||||
|
||||
return s;
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('bbcode', tinymce.plugins.BBCodePlugin);
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
!function(){tinymce.create("tinymce.plugins.BBCodePlugin",{init:function(a){var b=this,c=a.getParam("bbcode_dialect","punbb").toLowerCase();a.on("beforeSetContent",function(a){a.content=b["_"+c+"_bbcode2html"](a.content)}),a.on("postProcess",function(a){a.set&&(a.content=b["_"+c+"_bbcode2html"](a.content)),a.get&&(a.content=b["_"+c+"_html2bbcode"](a.content))})},getInfo:function(){return{longname:"BBCode Plugin",author:"Moxiecode Systems AB",authorurl:"http://www.tinymce.com",infourl:"http://www.tinymce.com/wiki.php/Plugin:bbcode"}},_punbb_html2bbcode:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),b(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),b(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),b(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),b(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),b(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),b(/<font>(.*?)<\/font>/gi,"$1"),b(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),b(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),b(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),b(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),b(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),b(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),b(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),b(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),b(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),b(/<\/(strong|b)>/gi,"[/b]"),b(/<(strong|b)>/gi,"[b]"),b(/<\/(em|i)>/gi,"[/i]"),b(/<(em|i)>/gi,"[i]"),b(/<\/u>/gi,"[/u]"),b(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),b(/<u>/gi,"[u]"),b(/<blockquote[^>]*>/gi,"[quote]"),b(/<\/blockquote>/gi,"[/quote]"),b(/<br \/>/gi,"\n"),b(/<br\/>/gi,"\n"),b(/<br>/gi,"\n"),b(/<p>/gi,""),b(/<\/p>/gi,"\n"),b(/ |\u00a0/gi," "),b(/"/gi,'"'),b(/</gi,"<"),b(/>/gi,">"),b(/&/gi,"&"),a},_punbb_bbcode2html:function(a){function b(b,c){a=a.replace(b,c)}return a=tinymce.trim(a),b(/\n/gi,"<br />"),b(/\[b\]/gi,"<strong>"),b(/\[\/b\]/gi,"</strong>"),b(/\[i\]/gi,"<em>"),b(/\[\/i\]/gi,"</em>"),b(/\[u\]/gi,"<u>"),b(/\[\/u\]/gi,"</u>"),b(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),b(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),b(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),b(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),b(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span> '),b(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span> '),a}}),tinymce.PluginManager.add("bbcode",tinymce.plugins.BBCodePlugin)}();
|
|
@ -0,0 +1,370 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('charmap', function(editor) {
|
||||
var charmap = [
|
||||
['160', 'no-break space'],
|
||||
['38', 'ampersand'],
|
||||
['34', 'quotation mark'],
|
||||
// finance
|
||||
['162', 'cent sign'],
|
||||
['8364', 'euro sign'],
|
||||
['163', 'pound sign'],
|
||||
['165', 'yen sign'],
|
||||
// signs
|
||||
['169', 'copyright sign'],
|
||||
['174', 'registered sign'],
|
||||
['8482', 'trade mark sign'],
|
||||
['8240', 'per mille sign'],
|
||||
['181', 'micro sign'],
|
||||
['183', 'middle dot'],
|
||||
['8226', 'bullet'],
|
||||
['8230', 'three dot leader'],
|
||||
['8242', 'minutes / feet'],
|
||||
['8243', 'seconds / inches'],
|
||||
['167', 'section sign'],
|
||||
['182', 'paragraph sign'],
|
||||
['223', 'sharp s / ess-zed'],
|
||||
// quotations
|
||||
['8249', 'single left-pointing angle quotation mark'],
|
||||
['8250', 'single right-pointing angle quotation mark'],
|
||||
['171', 'left pointing guillemet'],
|
||||
['187', 'right pointing guillemet'],
|
||||
['8216', 'left single quotation mark'],
|
||||
['8217', 'right single quotation mark'],
|
||||
['8220', 'left double quotation mark'],
|
||||
['8221', 'right double quotation mark'],
|
||||
['8218', 'single low-9 quotation mark'],
|
||||
['8222', 'double low-9 quotation mark'],
|
||||
['60', 'less-than sign'],
|
||||
['62', 'greater-than sign'],
|
||||
['8804', 'less-than or equal to'],
|
||||
['8805', 'greater-than or equal to'],
|
||||
['8211', 'en dash'],
|
||||
['8212', 'em dash'],
|
||||
['175', 'macron'],
|
||||
['8254', 'overline'],
|
||||
['164', 'currency sign'],
|
||||
['166', 'broken bar'],
|
||||
['168', 'diaeresis'],
|
||||
['161', 'inverted exclamation mark'],
|
||||
['191', 'turned question mark'],
|
||||
['710', 'circumflex accent'],
|
||||
['732', 'small tilde'],
|
||||
['176', 'degree sign'],
|
||||
['8722', 'minus sign'],
|
||||
['177', 'plus-minus sign'],
|
||||
['247', 'division sign'],
|
||||
['8260', 'fraction slash'],
|
||||
['215', 'multiplication sign'],
|
||||
['185', 'superscript one'],
|
||||
['178', 'superscript two'],
|
||||
['179', 'superscript three'],
|
||||
['188', 'fraction one quarter'],
|
||||
['189', 'fraction one half'],
|
||||
['190', 'fraction three quarters'],
|
||||
// math / logical
|
||||
['402', 'function / florin'],
|
||||
['8747', 'integral'],
|
||||
['8721', 'n-ary sumation'],
|
||||
['8734', 'infinity'],
|
||||
['8730', 'square root'],
|
||||
['8764', 'similar to'],
|
||||
['8773', 'approximately equal to'],
|
||||
['8776', 'almost equal to'],
|
||||
['8800', 'not equal to'],
|
||||
['8801', 'identical to'],
|
||||
['8712', 'element of'],
|
||||
['8713', 'not an element of'],
|
||||
['8715', 'contains as member'],
|
||||
['8719', 'n-ary product'],
|
||||
['8743', 'logical and'],
|
||||
['8744', 'logical or'],
|
||||
['172', 'not sign'],
|
||||
['8745', 'intersection'],
|
||||
['8746', 'union'],
|
||||
['8706', 'partial differential'],
|
||||
['8704', 'for all'],
|
||||
['8707', 'there exists'],
|
||||
['8709', 'diameter'],
|
||||
['8711', 'backward difference'],
|
||||
['8727', 'asterisk operator'],
|
||||
['8733', 'proportional to'],
|
||||
['8736', 'angle'],
|
||||
// undefined
|
||||
['180', 'acute accent'],
|
||||
['184', 'cedilla'],
|
||||
['170', 'feminine ordinal indicator'],
|
||||
['186', 'masculine ordinal indicator'],
|
||||
['8224', 'dagger'],
|
||||
['8225', 'double dagger'],
|
||||
// alphabetical special chars
|
||||
['192', 'A - grave'],
|
||||
['193', 'A - acute'],
|
||||
['194', 'A - circumflex'],
|
||||
['195', 'A - tilde'],
|
||||
['196', 'A - diaeresis'],
|
||||
['197', 'A - ring above'],
|
||||
['198', 'ligature AE'],
|
||||
['199', 'C - cedilla'],
|
||||
['200', 'E - grave'],
|
||||
['201', 'E - acute'],
|
||||
['202', 'E - circumflex'],
|
||||
['203', 'E - diaeresis'],
|
||||
['204', 'I - grave'],
|
||||
['205', 'I - acute'],
|
||||
['206', 'I - circumflex'],
|
||||
['207', 'I - diaeresis'],
|
||||
['208', 'ETH'],
|
||||
['209', 'N - tilde'],
|
||||
['210', 'O - grave'],
|
||||
['211', 'O - acute'],
|
||||
['212', 'O - circumflex'],
|
||||
['213', 'O - tilde'],
|
||||
['214', 'O - diaeresis'],
|
||||
['216', 'O - slash'],
|
||||
['338', 'ligature OE'],
|
||||
['352', 'S - caron'],
|
||||
['217', 'U - grave'],
|
||||
['218', 'U - acute'],
|
||||
['219', 'U - circumflex'],
|
||||
['220', 'U - diaeresis'],
|
||||
['221', 'Y - acute'],
|
||||
['376', 'Y - diaeresis'],
|
||||
['222', 'THORN'],
|
||||
['224', 'a - grave'],
|
||||
['225', 'a - acute'],
|
||||
['226', 'a - circumflex'],
|
||||
['227', 'a - tilde'],
|
||||
['228', 'a - diaeresis'],
|
||||
['229', 'a - ring above'],
|
||||
['230', 'ligature ae'],
|
||||
['231', 'c - cedilla'],
|
||||
['232', 'e - grave'],
|
||||
['233', 'e - acute'],
|
||||
['234', 'e - circumflex'],
|
||||
['235', 'e - diaeresis'],
|
||||
['236', 'i - grave'],
|
||||
['237', 'i - acute'],
|
||||
['238', 'i - circumflex'],
|
||||
['239', 'i - diaeresis'],
|
||||
['240', 'eth'],
|
||||
['241', 'n - tilde'],
|
||||
['242', 'o - grave'],
|
||||
['243', 'o - acute'],
|
||||
['244', 'o - circumflex'],
|
||||
['245', 'o - tilde'],
|
||||
['246', 'o - diaeresis'],
|
||||
['248', 'o slash'],
|
||||
['339', 'ligature oe'],
|
||||
['353', 's - caron'],
|
||||
['249', 'u - grave'],
|
||||
['250', 'u - acute'],
|
||||
['251', 'u - circumflex'],
|
||||
['252', 'u - diaeresis'],
|
||||
['253', 'y - acute'],
|
||||
['254', 'thorn'],
|
||||
['255', 'y - diaeresis'],
|
||||
['913', 'Alpha'],
|
||||
['914', 'Beta'],
|
||||
['915', 'Gamma'],
|
||||
['916', 'Delta'],
|
||||
['917', 'Epsilon'],
|
||||
['918', 'Zeta'],
|
||||
['919', 'Eta'],
|
||||
['920', 'Theta'],
|
||||
['921', 'Iota'],
|
||||
['922', 'Kappa'],
|
||||
['923', 'Lambda'],
|
||||
['924', 'Mu'],
|
||||
['925', 'Nu'],
|
||||
['926', 'Xi'],
|
||||
['927', 'Omicron'],
|
||||
['928', 'Pi'],
|
||||
['929', 'Rho'],
|
||||
['931', 'Sigma'],
|
||||
['932', 'Tau'],
|
||||
['933', 'Upsilon'],
|
||||
['934', 'Phi'],
|
||||
['935', 'Chi'],
|
||||
['936', 'Psi'],
|
||||
['937', 'Omega'],
|
||||
['945', 'alpha'],
|
||||
['946', 'beta'],
|
||||
['947', 'gamma'],
|
||||
['948', 'delta'],
|
||||
['949', 'epsilon'],
|
||||
['950', 'zeta'],
|
||||
['951', 'eta'],
|
||||
['952', 'theta'],
|
||||
['953', 'iota'],
|
||||
['954', 'kappa'],
|
||||
['955', 'lambda'],
|
||||
['956', 'mu'],
|
||||
['957', 'nu'],
|
||||
['958', 'xi'],
|
||||
['959', 'omicron'],
|
||||
['960', 'pi'],
|
||||
['961', 'rho'],
|
||||
['962', 'final sigma'],
|
||||
['963', 'sigma'],
|
||||
['964', 'tau'],
|
||||
['965', 'upsilon'],
|
||||
['966', 'phi'],
|
||||
['967', 'chi'],
|
||||
['968', 'psi'],
|
||||
['969', 'omega'],
|
||||
// symbols
|
||||
['8501', 'alef symbol'],
|
||||
['982', 'pi symbol'],
|
||||
['8476', 'real part symbol'],
|
||||
['978', 'upsilon - hook symbol'],
|
||||
['8472', 'Weierstrass p'],
|
||||
['8465', 'imaginary part'],
|
||||
// arrows
|
||||
['8592', 'leftwards arrow'],
|
||||
['8593', 'upwards arrow'],
|
||||
['8594', 'rightwards arrow'],
|
||||
['8595', 'downwards arrow'],
|
||||
['8596', 'left right arrow'],
|
||||
['8629', 'carriage return'],
|
||||
['8656', 'leftwards double arrow'],
|
||||
['8657', 'upwards double arrow'],
|
||||
['8658', 'rightwards double arrow'],
|
||||
['8659', 'downwards double arrow'],
|
||||
['8660', 'left right double arrow'],
|
||||
['8756', 'therefore'],
|
||||
['8834', 'subset of'],
|
||||
['8835', 'superset of'],
|
||||
['8836', 'not a subset of'],
|
||||
['8838', 'subset of or equal to'],
|
||||
['8839', 'superset of or equal to'],
|
||||
['8853', 'circled plus'],
|
||||
['8855', 'circled times'],
|
||||
['8869', 'perpendicular'],
|
||||
['8901', 'dot operator'],
|
||||
['8968', 'left ceiling'],
|
||||
['8969', 'right ceiling'],
|
||||
['8970', 'left floor'],
|
||||
['8971', 'right floor'],
|
||||
['9001', 'left-pointing angle bracket'],
|
||||
['9002', 'right-pointing angle bracket'],
|
||||
['9674', 'lozenge'],
|
||||
['9824', 'black spade suit'],
|
||||
['9827', 'black club suit'],
|
||||
['9829', 'black heart suit'],
|
||||
['9830', 'black diamond suit'],
|
||||
['8194', 'en space'],
|
||||
['8195', 'em space'],
|
||||
['8201', 'thin space'],
|
||||
['8204', 'zero width non-joiner'],
|
||||
['8205', 'zero width joiner'],
|
||||
['8206', 'left-to-right mark'],
|
||||
['8207', 'right-to-left mark'],
|
||||
['173', 'soft hyphen']
|
||||
];
|
||||
|
||||
function showDialog() {
|
||||
var gridHtml, x, y, win;
|
||||
|
||||
function getParentTd(elm) {
|
||||
while (elm) {
|
||||
if (elm.nodeName == 'TD') {
|
||||
return elm;
|
||||
}
|
||||
|
||||
elm = elm.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
gridHtml = '<table role="presentation" cellspacing="0" class="mce-charmap"><tbody>';
|
||||
|
||||
var width = 25;
|
||||
for (y = 0; y < 10; y++) {
|
||||
gridHtml += '<tr>';
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
var chr = charmap[y * width + x];
|
||||
|
||||
gridHtml += '<td title="' + chr[1] + '"><div tabindex="-1" title="' + chr[1] + '" role="button">' +
|
||||
(chr ? String.fromCharCode(parseInt(chr[0], 10)) : ' ') + '</div></td>';
|
||||
}
|
||||
|
||||
gridHtml += '</tr>';
|
||||
}
|
||||
|
||||
gridHtml += '</tbody></table>';
|
||||
|
||||
var charMapPanel = {
|
||||
type: 'container',
|
||||
html: gridHtml,
|
||||
onclick: function(e) {
|
||||
var target = e.target;
|
||||
|
||||
if (target.tagName == 'TD') {
|
||||
target = target.firstChild;
|
||||
}
|
||||
|
||||
if (target.tagName == 'DIV') {
|
||||
editor.execCommand('mceInsertContent', false, target.firstChild.data);
|
||||
|
||||
if (!e.ctrlKey) {
|
||||
win.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
onmouseover: function(e) {
|
||||
var td = getParentTd(e.target);
|
||||
|
||||
if (td) {
|
||||
win.find('#preview').text(td.firstChild.firstChild.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
win = editor.windowManager.open({
|
||||
title: "Special character",
|
||||
spacing: 10,
|
||||
padding: 10,
|
||||
items: [
|
||||
charMapPanel,
|
||||
{
|
||||
type: 'label',
|
||||
name: 'preview',
|
||||
text: ' ',
|
||||
style: 'font-size: 40px; text-align: center',
|
||||
border: 1,
|
||||
minWidth: 100,
|
||||
minHeight: 80
|
||||
}
|
||||
],
|
||||
buttons: [
|
||||
{text: "Close", onclick: function() {
|
||||
win.close();
|
||||
}}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
editor.addButton('charmap', {
|
||||
icon: 'charmap',
|
||||
tooltip: 'Special character',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
editor.addMenuItem('charmap', {
|
||||
icon: 'charmap',
|
||||
text: 'Special character',
|
||||
onclick: showDialog,
|
||||
context: 'insert'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('code', function(editor) {
|
||||
function showDialog() {
|
||||
var win = editor.windowManager.open({
|
||||
title: "Source code",
|
||||
body: {
|
||||
type: 'textbox',
|
||||
name: 'code',
|
||||
multiline: true,
|
||||
minWidth: editor.getParam("code_dialog_width", 600),
|
||||
minHeight: editor.getParam("code_dialog_height", Math.min(tinymce.DOM.getViewPort().h - 200, 500)),
|
||||
spellcheck: false,
|
||||
style: 'direction: ltr; text-align: left'
|
||||
},
|
||||
onSubmit: function(e) {
|
||||
// We get a lovely "Wrong document" error in IE 11 if we
|
||||
// don't move the focus to the editor before creating an undo
|
||||
// transation since it tries to make a bookmark for the current selection
|
||||
editor.focus();
|
||||
|
||||
editor.undoManager.transact(function() {
|
||||
editor.setContent(e.data.code);
|
||||
});
|
||||
|
||||
editor.selection.setCursorLocation();
|
||||
editor.nodeChanged();
|
||||
}
|
||||
});
|
||||
|
||||
// Gecko has a major performance issue with textarea
|
||||
// contents so we need to set it when all reflows are done
|
||||
win.find('#code').value(editor.getContent({source_view: true}));
|
||||
}
|
||||
|
||||
editor.addCommand("mceCodeEditor", showDialog);
|
||||
|
||||
editor.addButton('code', {
|
||||
icon: 'code',
|
||||
tooltip: 'Source code',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
editor.addMenuItem('code', {
|
||||
icon: 'code',
|
||||
text: 'Source code',
|
||||
context: 'tools',
|
||||
onclick: showDialog
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("code",function(a){function b(){var b=a.windowManager.open({title:"Source code",body:{type:"textbox",name:"code",multiline:!0,minWidth:a.getParam("code_dialog_width",600),minHeight:a.getParam("code_dialog_height",Math.min(tinymce.DOM.getViewPort().h-200,500)),spellcheck:!1,style:"direction: ltr; text-align: left"},onSubmit:function(b){a.focus(),a.undoManager.transact(function(){a.setContent(b.data.code)}),a.selection.setCursorLocation(),a.nodeChanged()}});b.find("#code").value(a.getContent({source_view:!0}))}a.addCommand("mceCodeEditor",b),a.addButton("code",{icon:"code",tooltip:"Source code",onclick:b}),a.addMenuItem("code",{icon:"code",text:"Source code",context:"tools",onclick:b})});
|
|
@ -0,0 +1,112 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('colorpicker', function(editor) {
|
||||
function colorPickerCallback(callback, value) {
|
||||
function setColor(value) {
|
||||
var color = new tinymce.util.Color(value), rgb = color.toRgb();
|
||||
|
||||
win.fromJSON({
|
||||
r: rgb.r,
|
||||
g: rgb.g,
|
||||
b: rgb.b,
|
||||
hex: color.toHex().substr(1)
|
||||
});
|
||||
|
||||
showPreview(color.toHex());
|
||||
}
|
||||
|
||||
function showPreview(hexColor) {
|
||||
win.find('#preview')[0].getEl().style.background = hexColor;
|
||||
}
|
||||
|
||||
var win = editor.windowManager.open({
|
||||
title: 'Color',
|
||||
items: {
|
||||
type: 'container',
|
||||
layout: 'flex',
|
||||
direction: 'row',
|
||||
align: 'stretch',
|
||||
padding: 5,
|
||||
spacing: 10,
|
||||
items: [
|
||||
{
|
||||
type: 'colorpicker',
|
||||
value: value,
|
||||
onchange: function() {
|
||||
var rgb = this.rgb();
|
||||
|
||||
if (win) {
|
||||
win.find('#r').value(rgb.r);
|
||||
win.find('#g').value(rgb.g);
|
||||
win.find('#b').value(rgb.b);
|
||||
win.find('#hex').value(this.value().substr(1));
|
||||
showPreview(this.value());
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
padding: 0,
|
||||
labelGap: 5,
|
||||
defaults: {
|
||||
type: 'textbox',
|
||||
size: 7,
|
||||
value: '0',
|
||||
flex: 1,
|
||||
spellcheck: false,
|
||||
onchange: function() {
|
||||
var colorPickerCtrl = win.find('colorpicker')[0];
|
||||
var name, value;
|
||||
|
||||
name = this.name();
|
||||
value = this.value();
|
||||
|
||||
if (name == "hex") {
|
||||
value = '#' + value;
|
||||
setColor(value);
|
||||
colorPickerCtrl.value(value);
|
||||
return;
|
||||
}
|
||||
|
||||
value = {
|
||||
r: win.find('#r').value(),
|
||||
g: win.find('#g').value(),
|
||||
b: win.find('#b').value()
|
||||
};
|
||||
|
||||
colorPickerCtrl.value(value);
|
||||
setColor(value);
|
||||
}
|
||||
},
|
||||
items: [
|
||||
{name: 'r', label: 'R', autofocus: 1},
|
||||
{name: 'g', label: 'G'},
|
||||
{name: 'b', label: 'B'},
|
||||
{name: 'hex', label: '#', value: '000000'},
|
||||
{name: 'preview', type: 'container', border: 1}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
onSubmit: function() {
|
||||
callback('#' + this.toJSON().hex);
|
||||
}
|
||||
});
|
||||
|
||||
setColor(value);
|
||||
}
|
||||
|
||||
if (!editor.settings.color_picker_callback) {
|
||||
editor.settings.color_picker_callback = colorPickerCallback;
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("colorpicker",function(a){function b(b,c){function d(a){var b=new tinymce.util.Color(a),c=b.toRgb();f.fromJSON({r:c.r,g:c.g,b:c.b,hex:b.toHex().substr(1)}),e(b.toHex())}function e(a){f.find("#preview")[0].getEl().style.background=a}var f=a.windowManager.open({title:"Color",items:{type:"container",layout:"flex",direction:"row",align:"stretch",padding:5,spacing:10,items:[{type:"colorpicker",value:c,onchange:function(){var a=this.rgb();f&&(f.find("#r").value(a.r),f.find("#g").value(a.g),f.find("#b").value(a.b),f.find("#hex").value(this.value().substr(1)),e(this.value()))}},{type:"form",padding:0,labelGap:5,defaults:{type:"textbox",size:7,value:"0",flex:1,spellcheck:!1,onchange:function(){var a,b,c=f.find("colorpicker")[0];return a=this.name(),b=this.value(),"hex"==a?(b="#"+b,d(b),void c.value(b)):(b={r:f.find("#r").value(),g:f.find("#g").value(),b:f.find("#b").value()},c.value(b),void d(b))}},items:[{name:"r",label:"R",autofocus:1},{name:"g",label:"G"},{name:"b",label:"B"},{name:"hex",label:"#",value:"000000"},{name:"preview",type:"container",border:1}]}]},onSubmit:function(){b("#"+this.toJSON().hex)}});d(c)}a.settings.color_picker_callback||(a.settings.color_picker_callback=b)});
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('contextmenu', function(editor) {
|
||||
var menu, contextmenuNeverUseNative = editor.settings.contextmenu_never_use_native;
|
||||
|
||||
editor.on('contextmenu', function(e) {
|
||||
var contextmenu, doc = editor.getDoc();
|
||||
|
||||
// Block TinyMCE menu on ctrlKey
|
||||
if (e.ctrlKey && !contextmenuNeverUseNative) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
/**
|
||||
* WebKit/Blink on Mac has the odd behavior of selecting the target word or line this causes
|
||||
* issues when for example inserting images see: #7022
|
||||
*/
|
||||
if (tinymce.Env.mac && tinymce.Env.webkit) {
|
||||
if (e.button == 2 && doc.caretRangeFromPoint) {
|
||||
editor.selection.setRng(doc.caretRangeFromPoint(e.x, e.y));
|
||||
}
|
||||
}
|
||||
|
||||
contextmenu = editor.settings.contextmenu || 'link image inserttable | cell row column deletetable';
|
||||
|
||||
// Render menu
|
||||
if (!menu) {
|
||||
var items = [];
|
||||
|
||||
tinymce.each(contextmenu.split(/[ ,]/), function(name) {
|
||||
var item = editor.menuItems[name];
|
||||
|
||||
if (name == '|') {
|
||||
item = {text: name};
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item.shortcut = ''; // Hide shortcuts
|
||||
items.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].text == '|') {
|
||||
if (i === 0 || i == items.length - 1) {
|
||||
items.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu = new tinymce.ui.Menu({
|
||||
items: items,
|
||||
context: 'contextmenu'
|
||||
}).addClass('contextmenu').renderTo();
|
||||
|
||||
editor.on('remove', function() {
|
||||
menu.remove();
|
||||
menu = null;
|
||||
});
|
||||
} else {
|
||||
menu.show();
|
||||
}
|
||||
|
||||
// Position menu
|
||||
var pos = {x: e.pageX, y: e.pageY};
|
||||
|
||||
if (!editor.inline) {
|
||||
pos = tinymce.DOM.getPos(editor.getContentAreaContainer());
|
||||
pos.x += e.clientX;
|
||||
pos.y += e.clientY;
|
||||
}
|
||||
|
||||
menu.moveTo(pos.x, pos.y);
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("contextmenu",function(a){var b,c=a.settings.contextmenu_never_use_native;a.on("contextmenu",function(d){var e,f=a.getDoc();if(!d.ctrlKey||c){if(d.preventDefault(),tinymce.Env.mac&&tinymce.Env.webkit&&2==d.button&&f.caretRangeFromPoint&&a.selection.setRng(f.caretRangeFromPoint(d.x,d.y)),e=a.settings.contextmenu||"link image inserttable | cell row column deletetable",b)b.show();else{var g=[];tinymce.each(e.split(/[ ,]/),function(b){var c=a.menuItems[b];"|"==b&&(c={text:b}),c&&(c.shortcut="",g.push(c))});for(var h=0;h<g.length;h++)"|"==g[h].text&&(0===h||h==g.length-1)&&g.splice(h,1);b=new tinymce.ui.Menu({items:g,context:"contextmenu"}).addClass("contextmenu").renderTo(),a.on("remove",function(){b.remove(),b=null})}var i={x:d.pageX,y:d.pageY};a.inline||(i=tinymce.DOM.getPos(a.getContentAreaContainer()),i.x+=d.clientX,i.y+=d.clientY),b.moveTo(i.x,i.y)}})});
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('directionality', function(editor) {
|
||||
function setDir(dir) {
|
||||
var dom = editor.dom, curDir, blocks = editor.selection.getSelectedBlocks();
|
||||
|
||||
if (blocks.length) {
|
||||
curDir = dom.getAttrib(blocks[0], "dir");
|
||||
|
||||
tinymce.each(blocks, function(block) {
|
||||
// Add dir to block if the parent block doesn't already have that dir
|
||||
if (!dom.getParent(block.parentNode, "*[dir='" + dir + "']", dom.getRoot())) {
|
||||
if (curDir != dir) {
|
||||
dom.setAttrib(block, "dir", dir);
|
||||
} else {
|
||||
dom.setAttrib(block, "dir", null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.nodeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
function generateSelector(dir) {
|
||||
var selector = [];
|
||||
|
||||
tinymce.each('h1 h2 h3 h4 h5 h6 div p'.split(' '), function(name) {
|
||||
selector.push(name + '[dir=' + dir + ']');
|
||||
});
|
||||
|
||||
return selector.join(',');
|
||||
}
|
||||
|
||||
editor.addCommand('mceDirectionLTR', function() {
|
||||
setDir("ltr");
|
||||
});
|
||||
|
||||
editor.addCommand('mceDirectionRTL', function() {
|
||||
setDir("rtl");
|
||||
});
|
||||
|
||||
editor.addButton('ltr', {
|
||||
title: 'Left to right',
|
||||
cmd: 'mceDirectionLTR',
|
||||
stateSelector: generateSelector('ltr')
|
||||
});
|
||||
|
||||
editor.addButton('rtl', {
|
||||
title: 'Right to left',
|
||||
cmd: 'mceDirectionRTL',
|
||||
stateSelector: generateSelector('rtl')
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("directionality",function(a){function b(b){var c,d=a.dom,e=a.selection.getSelectedBlocks();e.length&&(c=d.getAttrib(e[0],"dir"),tinymce.each(e,function(a){d.getParent(a.parentNode,"*[dir='"+b+"']",d.getRoot())||(c!=b?d.setAttrib(a,"dir",b):d.setAttrib(a,"dir",null))}),a.nodeChanged())}function c(a){var b=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(c){b.push(c+"[dir="+a+"]")}),b.join(",")}a.addCommand("mceDirectionLTR",function(){b("ltr")}),a.addCommand("mceDirectionRTL",function(){b("rtl")}),a.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:c("ltr")}),a.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:c("rtl")})});
|
After Width: | Height: | Size: 354 B |
After Width: | Height: | Size: 329 B |
After Width: | Height: | Size: 331 B |
After Width: | Height: | Size: 342 B |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 336 B |
After Width: | Height: | Size: 338 B |
After Width: | Height: | Size: 343 B |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 323 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 338 B |
After Width: | Height: | Size: 328 B |
After Width: | Height: | Size: 337 B |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 336 B |
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('emoticons', function(editor, url) {
|
||||
var emoticons = [
|
||||
["cool", "cry", "embarassed", "foot-in-mouth"],
|
||||
["frown", "innocent", "kiss", "laughing"],
|
||||
["money-mouth", "sealed", "smile", "surprised"],
|
||||
["tongue-out", "undecided", "wink", "yell"]
|
||||
];
|
||||
|
||||
function getHtml() {
|
||||
var emoticonsHtml;
|
||||
|
||||
emoticonsHtml = '<table role="list" class="mce-grid">';
|
||||
|
||||
tinymce.each(emoticons, function(row) {
|
||||
emoticonsHtml += '<tr>';
|
||||
|
||||
tinymce.each(row, function(icon) {
|
||||
var emoticonUrl = url + '/img/smiley-' + icon + '.gif';
|
||||
|
||||
emoticonsHtml += '<td><a href="#" data-mce-url="' + emoticonUrl + '" data-mce-alt="' + icon + '" tabindex="-1" ' +
|
||||
'role="option" aria-label="' + icon + '"><img src="' +
|
||||
emoticonUrl + '" style="width: 18px; height: 18px" role="presentation" /></a></td>';
|
||||
});
|
||||
|
||||
emoticonsHtml += '</tr>';
|
||||
});
|
||||
|
||||
emoticonsHtml += '</table>';
|
||||
|
||||
return emoticonsHtml;
|
||||
}
|
||||
|
||||
editor.addButton('emoticons', {
|
||||
type: 'panelbutton',
|
||||
panel: {
|
||||
role: 'application',
|
||||
autohide: true,
|
||||
html: getHtml,
|
||||
onclick: function(e) {
|
||||
var linkElm = editor.dom.getParent(e.target, 'a');
|
||||
|
||||
if (linkElm) {
|
||||
editor.insertContent(
|
||||
'<img src="' + linkElm.getAttribute('data-mce-url') + '" alt="' + linkElm.getAttribute('data-mce-alt') + '" />'
|
||||
);
|
||||
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: 'Emoticons'
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("emoticons",function(a,b){function c(){var a;return a='<table role="list" class="mce-grid">',tinymce.each(d,function(c){a+="<tr>",tinymce.each(c,function(c){var d=b+"/img/smiley-"+c+".gif";a+='<td><a href="#" data-mce-url="'+d+'" data-mce-alt="'+c+'" tabindex="-1" role="option" aria-label="'+c+'"><img src="'+d+'" style="width: 18px; height: 18px" role="presentation" /></a></td>'}),a+="</tr>"}),a+="</table>"}var d=[["cool","cry","embarassed","foot-in-mouth"],["frown","innocent","kiss","laughing"],["money-mouth","sealed","smile","surprised"],["tongue-out","undecided","wink","yell"]];a.addButton("emoticons",{type:"panelbutton",panel:{role:"application",autohide:!0,html:c,onclick:function(b){var c=a.dom.getParent(b.target,"a");c&&(a.insertContent('<img src="'+c.getAttribute("data-mce-url")+'" alt="'+c.getAttribute("data-mce-alt")+'" />'),this.hide())}},tooltip:"Emoticons"})});
|
|
@ -0,0 +1,490 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('fullpage', function(editor) {
|
||||
var each = tinymce.each, Node = tinymce.html.Node;
|
||||
var head, foot;
|
||||
|
||||
function showDialog() {
|
||||
var data = htmlToData();
|
||||
|
||||
editor.windowManager.open({
|
||||
title: 'Document properties',
|
||||
data: data,
|
||||
defaults: {type: 'textbox', size: 40},
|
||||
body: [
|
||||
{name: 'title', label: 'Title'},
|
||||
{name: 'keywords', label: 'Keywords'},
|
||||
{name: 'description', label: 'Description'},
|
||||
{name: 'robots', label: 'Robots'},
|
||||
{name: 'author', label: 'Author'},
|
||||
{name: 'docencoding', label: 'Encoding'}
|
||||
],
|
||||
onSubmit: function(e) {
|
||||
dataToHtml(tinymce.extend(data, e.data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function htmlToData() {
|
||||
var headerFragment = parseHeader(), data = {}, elm, matches;
|
||||
|
||||
function getAttr(elm, name) {
|
||||
var value = elm.attr(name);
|
||||
|
||||
return value || '';
|
||||
}
|
||||
|
||||
// Default some values
|
||||
data.fontface = editor.getParam("fullpage_default_fontface", "");
|
||||
data.fontsize = editor.getParam("fullpage_default_fontsize", "");
|
||||
|
||||
// Parse XML PI
|
||||
elm = headerFragment.firstChild;
|
||||
if (elm.type == 7) {
|
||||
data.xml_pi = true;
|
||||
matches = /encoding="([^"]+)"/.exec(elm.value);
|
||||
if (matches) {
|
||||
data.docencoding = matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Parse doctype
|
||||
elm = headerFragment.getAll('#doctype')[0];
|
||||
if (elm) {
|
||||
data.doctype = '<!DOCTYPE' + elm.value + ">";
|
||||
}
|
||||
|
||||
// Parse title element
|
||||
elm = headerFragment.getAll('title')[0];
|
||||
if (elm && elm.firstChild) {
|
||||
data.title = elm.firstChild.value;
|
||||
}
|
||||
|
||||
// Parse meta elements
|
||||
each(headerFragment.getAll('meta'), function(meta) {
|
||||
var name = meta.attr('name'), httpEquiv = meta.attr('http-equiv'), matches;
|
||||
|
||||
if (name) {
|
||||
data[name.toLowerCase()] = meta.attr('content');
|
||||
} else if (httpEquiv == "Content-Type") {
|
||||
matches = /charset\s*=\s*(.*)\s*/gi.exec(meta.attr('content'));
|
||||
|
||||
if (matches) {
|
||||
data.docencoding = matches[1];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Parse html attribs
|
||||
elm = headerFragment.getAll('html')[0];
|
||||
if (elm) {
|
||||
data.langcode = getAttr(elm, 'lang') || getAttr(elm, 'xml:lang');
|
||||
}
|
||||
|
||||
// Parse stylesheets
|
||||
data.stylesheets = [];
|
||||
tinymce.each(headerFragment.getAll('link'), function(link) {
|
||||
if (link.attr('rel') == 'stylesheet') {
|
||||
data.stylesheets.push(link.attr('href'));
|
||||
}
|
||||
});
|
||||
|
||||
// Parse body parts
|
||||
elm = headerFragment.getAll('body')[0];
|
||||
if (elm) {
|
||||
data.langdir = getAttr(elm, 'dir');
|
||||
data.style = getAttr(elm, 'style');
|
||||
data.visited_color = getAttr(elm, 'vlink');
|
||||
data.link_color = getAttr(elm, 'link');
|
||||
data.active_color = getAttr(elm, 'alink');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function dataToHtml(data) {
|
||||
var headerFragment, headElement, html, elm, value, dom = editor.dom;
|
||||
|
||||
function setAttr(elm, name, value) {
|
||||
elm.attr(name, value ? value : undefined);
|
||||
}
|
||||
|
||||
function addHeadNode(node) {
|
||||
if (headElement.firstChild) {
|
||||
headElement.insert(node, headElement.firstChild);
|
||||
} else {
|
||||
headElement.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
headerFragment = parseHeader();
|
||||
headElement = headerFragment.getAll('head')[0];
|
||||
if (!headElement) {
|
||||
elm = headerFragment.getAll('html')[0];
|
||||
headElement = new Node('head', 1);
|
||||
|
||||
if (elm.firstChild) {
|
||||
elm.insert(headElement, elm.firstChild, true);
|
||||
} else {
|
||||
elm.append(headElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Add/update/remove XML-PI
|
||||
elm = headerFragment.firstChild;
|
||||
if (data.xml_pi) {
|
||||
value = 'version="1.0"';
|
||||
|
||||
if (data.docencoding) {
|
||||
value += ' encoding="' + data.docencoding + '"';
|
||||
}
|
||||
|
||||
if (elm.type != 7) {
|
||||
elm = new Node('xml', 7);
|
||||
headerFragment.insert(elm, headerFragment.firstChild, true);
|
||||
}
|
||||
|
||||
elm.value = value;
|
||||
} else if (elm && elm.type == 7) {
|
||||
elm.remove();
|
||||
}
|
||||
|
||||
// Add/update/remove doctype
|
||||
elm = headerFragment.getAll('#doctype')[0];
|
||||
if (data.doctype) {
|
||||
if (!elm) {
|
||||
elm = new Node('#doctype', 10);
|
||||
|
||||
if (data.xml_pi) {
|
||||
headerFragment.insert(elm, headerFragment.firstChild);
|
||||
} else {
|
||||
addHeadNode(elm);
|
||||
}
|
||||
}
|
||||
|
||||
elm.value = data.doctype.substring(9, data.doctype.length - 1);
|
||||
} else if (elm) {
|
||||
elm.remove();
|
||||
}
|
||||
|
||||
// Add meta encoding
|
||||
elm = null;
|
||||
each(headerFragment.getAll('meta'), function(meta) {
|
||||
if (meta.attr('http-equiv') == 'Content-Type') {
|
||||
elm = meta;
|
||||
}
|
||||
});
|
||||
|
||||
if (data.docencoding) {
|
||||
if (!elm) {
|
||||
elm = new Node('meta', 1);
|
||||
elm.attr('http-equiv', 'Content-Type');
|
||||
elm.shortEnded = true;
|
||||
addHeadNode(elm);
|
||||
}
|
||||
|
||||
elm.attr('content', 'text/html; charset=' + data.docencoding);
|
||||
} else if (elm) {
|
||||
elm.remove();
|
||||
}
|
||||
|
||||
// Add/update/remove title
|
||||
elm = headerFragment.getAll('title')[0];
|
||||
if (data.title) {
|
||||
if (!elm) {
|
||||
elm = new Node('title', 1);
|
||||
addHeadNode(elm);
|
||||
} else {
|
||||
elm.empty();
|
||||
}
|
||||
|
||||
elm.append(new Node('#text', 3)).value = data.title;
|
||||
} else if (elm) {
|
||||
elm.remove();
|
||||
}
|
||||
|
||||
// Add/update/remove meta
|
||||
each('keywords,description,author,copyright,robots'.split(','), function(name) {
|
||||
var nodes = headerFragment.getAll('meta'), i, meta, value = data[name];
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
meta = nodes[i];
|
||||
|
||||
if (meta.attr('name') == name) {
|
||||
if (value) {
|
||||
meta.attr('content', value);
|
||||
} else {
|
||||
meta.remove();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (value) {
|
||||
elm = new Node('meta', 1);
|
||||
elm.attr('name', name);
|
||||
elm.attr('content', value);
|
||||
elm.shortEnded = true;
|
||||
|
||||
addHeadNode(elm);
|
||||
}
|
||||
});
|
||||
|
||||
var currentStyleSheetsMap = {};
|
||||
tinymce.each(headerFragment.getAll('link'), function(stylesheet) {
|
||||
if (stylesheet.attr('rel') == 'stylesheet') {
|
||||
currentStyleSheetsMap[stylesheet.attr('href')] = stylesheet;
|
||||
}
|
||||
});
|
||||
|
||||
// Add new
|
||||
tinymce.each(data.stylesheets, function(stylesheet) {
|
||||
if (!currentStyleSheetsMap[stylesheet]) {
|
||||
elm = new Node('link', 1);
|
||||
elm.attr({
|
||||
rel: 'stylesheet',
|
||||
text: 'text/css',
|
||||
href: stylesheet
|
||||
});
|
||||
elm.shortEnded = true;
|
||||
addHeadNode(elm);
|
||||
}
|
||||
|
||||
delete currentStyleSheetsMap[stylesheet];
|
||||
});
|
||||
|
||||
// Delete old
|
||||
tinymce.each(currentStyleSheetsMap, function(stylesheet) {
|
||||
stylesheet.remove();
|
||||
});
|
||||
|
||||
// Update body attributes
|
||||
elm = headerFragment.getAll('body')[0];
|
||||
if (elm) {
|
||||
setAttr(elm, 'dir', data.langdir);
|
||||
setAttr(elm, 'style', data.style);
|
||||
setAttr(elm, 'vlink', data.visited_color);
|
||||
setAttr(elm, 'link', data.link_color);
|
||||
setAttr(elm, 'alink', data.active_color);
|
||||
|
||||
// Update iframe body as well
|
||||
dom.setAttribs(editor.getBody(), {
|
||||
style : data.style,
|
||||
dir : data.dir,
|
||||
vLink : data.visited_color,
|
||||
link : data.link_color,
|
||||
aLink : data.active_color
|
||||
});
|
||||
}
|
||||
|
||||
// Set html attributes
|
||||
elm = headerFragment.getAll('html')[0];
|
||||
if (elm) {
|
||||
setAttr(elm, 'lang', data.langcode);
|
||||
setAttr(elm, 'xml:lang', data.langcode);
|
||||
}
|
||||
|
||||
// No need for a head element
|
||||
if (!headElement.firstChild) {
|
||||
headElement.remove();
|
||||
}
|
||||
|
||||
// Serialize header fragment and crop away body part
|
||||
html = new tinymce.html.Serializer({
|
||||
validate: false,
|
||||
indent: true,
|
||||
apply_source_formatting : true,
|
||||
indent_before: 'head,html,body,meta,title,script,link,style',
|
||||
indent_after: 'head,html,body,meta,title,script,link,style'
|
||||
}).serialize(headerFragment);
|
||||
|
||||
head = html.substring(0, html.indexOf('</body>'));
|
||||
}
|
||||
|
||||
function parseHeader() {
|
||||
// Parse the contents with a DOM parser
|
||||
return new tinymce.html.DomParser({
|
||||
validate: false,
|
||||
root_name: '#document'
|
||||
}).parse(head);
|
||||
}
|
||||
|
||||
function setContent(evt) {
|
||||
var startPos, endPos, content = evt.content, headerFragment, styles = '', dom = editor.dom, elm;
|
||||
|
||||
if (evt.selection) {
|
||||
return;
|
||||
}
|
||||
|
||||
function low(s) {
|
||||
return s.replace(/<\/?[A-Z]+/g, function(a) {
|
||||
return a.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
// Ignore raw updated if we already have a head, this will fix issues with undo/redo keeping the head/foot separate
|
||||
if (evt.format == 'raw' && head) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.source_view && editor.getParam('fullpage_hide_in_source_view')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fixed so new document/setContent('') doesn't remove existing header/footer except when it's in source code view
|
||||
if (content.length === 0 && !evt.source_view) {
|
||||
content = tinymce.trim(head) + '\n' + tinymce.trim(content) + '\n' + tinymce.trim(foot);
|
||||
}
|
||||
|
||||
// Parse out head, body and footer
|
||||
content = content.replace(/<(\/?)BODY/gi, '<$1body');
|
||||
startPos = content.indexOf('<body');
|
||||
|
||||
if (startPos != -1) {
|
||||
startPos = content.indexOf('>', startPos);
|
||||
head = low(content.substring(0, startPos + 1));
|
||||
|
||||
endPos = content.indexOf('</body', startPos);
|
||||
if (endPos == -1) {
|
||||
endPos = content.length;
|
||||
}
|
||||
|
||||
evt.content = content.substring(startPos + 1, endPos);
|
||||
foot = low(content.substring(endPos));
|
||||
} else {
|
||||
head = getDefaultHeader();
|
||||
foot = '\n</body>\n</html>';
|
||||
}
|
||||
|
||||
// Parse header and update iframe
|
||||
headerFragment = parseHeader();
|
||||
each(headerFragment.getAll('style'), function(node) {
|
||||
if (node.firstChild) {
|
||||
styles += node.firstChild.value;
|
||||
}
|
||||
});
|
||||
|
||||
elm = headerFragment.getAll('body')[0];
|
||||
if (elm) {
|
||||
dom.setAttribs(editor.getBody(), {
|
||||
style: elm.attr('style') || '',
|
||||
dir: elm.attr('dir') || '',
|
||||
vLink: elm.attr('vlink') || '',
|
||||
link: elm.attr('link') || '',
|
||||
aLink: elm.attr('alink') || ''
|
||||
});
|
||||
}
|
||||
|
||||
dom.remove('fullpage_styles');
|
||||
|
||||
var headElm = editor.getDoc().getElementsByTagName('head')[0];
|
||||
|
||||
if (styles) {
|
||||
dom.add(headElm, 'style', {
|
||||
id : 'fullpage_styles'
|
||||
}, styles);
|
||||
|
||||
// Needed for IE 6/7
|
||||
elm = dom.get('fullpage_styles');
|
||||
if (elm.styleSheet) {
|
||||
elm.styleSheet.cssText = styles;
|
||||
}
|
||||
}
|
||||
|
||||
var currentStyleSheetsMap = {};
|
||||
tinymce.each(headElm.getElementsByTagName('link'), function(stylesheet) {
|
||||
if (stylesheet.rel == 'stylesheet' && stylesheet.getAttribute('data-mce-fullpage')) {
|
||||
currentStyleSheetsMap[stylesheet.href] = stylesheet;
|
||||
}
|
||||
});
|
||||
|
||||
// Add new
|
||||
tinymce.each(headerFragment.getAll('link'), function(stylesheet) {
|
||||
var href = stylesheet.attr('href');
|
||||
|
||||
if (!currentStyleSheetsMap[href] && stylesheet.attr('rel') == 'stylesheet') {
|
||||
dom.add(headElm, 'link', {
|
||||
rel: 'stylesheet',
|
||||
text: 'text/css',
|
||||
href: href,
|
||||
'data-mce-fullpage': '1'
|
||||
});
|
||||
}
|
||||
|
||||
delete currentStyleSheetsMap[href];
|
||||
});
|
||||
|
||||
// Delete old
|
||||
tinymce.each(currentStyleSheetsMap, function(stylesheet) {
|
||||
stylesheet.parentNode.removeChild(stylesheet);
|
||||
});
|
||||
}
|
||||
|
||||
function getDefaultHeader() {
|
||||
var header = '', value, styles = '';
|
||||
|
||||
if (editor.getParam('fullpage_default_xml_pi')) {
|
||||
header += '<?xml version="1.0" encoding="' + editor.getParam('fullpage_default_encoding', 'ISO-8859-1') + '" ?>\n';
|
||||
}
|
||||
|
||||
header += editor.getParam('fullpage_default_doctype', '<!DOCTYPE html>');
|
||||
header += '\n<html>\n<head>\n';
|
||||
|
||||
if ((value = editor.getParam('fullpage_default_title'))) {
|
||||
header += '<title>' + value + '</title>\n';
|
||||
}
|
||||
|
||||
if ((value = editor.getParam('fullpage_default_encoding'))) {
|
||||
header += '<meta http-equiv="Content-Type" content="text/html; charset=' + value + '" />\n';
|
||||
}
|
||||
|
||||
if ((value = editor.getParam('fullpage_default_font_family'))) {
|
||||
styles += 'font-family: ' + value + ';';
|
||||
}
|
||||
|
||||
if ((value = editor.getParam('fullpage_default_font_size'))) {
|
||||
styles += 'font-size: ' + value + ';';
|
||||
}
|
||||
|
||||
if ((value = editor.getParam('fullpage_default_text_color'))) {
|
||||
styles += 'color: ' + value + ';';
|
||||
}
|
||||
|
||||
header += '</head>\n<body' + (styles ? ' style="' + styles + '"' : '') + '>\n';
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
function getContent(evt) {
|
||||
if (!evt.selection && (!evt.source_view || !editor.getParam('fullpage_hide_in_source_view'))) {
|
||||
evt.content = tinymce.trim(head) + '\n' + tinymce.trim(evt.content) + '\n' + tinymce.trim(foot);
|
||||
}
|
||||
}
|
||||
|
||||
editor.addCommand('mceFullPageProperties', showDialog);
|
||||
|
||||
editor.addButton('fullpage', {
|
||||
title: 'Document properties',
|
||||
cmd : 'mceFullPageProperties'
|
||||
});
|
||||
|
||||
editor.addMenuItem('fullpage', {
|
||||
text: 'Document properties',
|
||||
cmd : 'mceFullPageProperties',
|
||||
context: 'file'
|
||||
});
|
||||
|
||||
editor.on('BeforeSetContent', setContent);
|
||||
editor.on('GetContent', getContent);
|
||||
});
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('fullscreen', function(editor) {
|
||||
var fullscreenState = false, DOM = tinymce.DOM, iframeWidth, iframeHeight, resizeHandler;
|
||||
var containerWidth, containerHeight;
|
||||
|
||||
if (editor.settings.inline) {
|
||||
return;
|
||||
}
|
||||
|
||||
function getWindowSize() {
|
||||
var w, h, win = window, doc = document;
|
||||
var body = doc.body;
|
||||
|
||||
// Old IE
|
||||
if (body.offsetWidth) {
|
||||
w = body.offsetWidth;
|
||||
h = body.offsetHeight;
|
||||
}
|
||||
|
||||
// Modern browsers
|
||||
if (win.innerWidth && win.innerHeight) {
|
||||
w = win.innerWidth;
|
||||
h = win.innerHeight;
|
||||
}
|
||||
|
||||
return {w: w, h: h};
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
var body = document.body, documentElement = document.documentElement, editorContainerStyle;
|
||||
var editorContainer, iframe, iframeStyle;
|
||||
|
||||
function resize() {
|
||||
DOM.setStyle(iframe, 'height', getWindowSize().h - (editorContainer.clientHeight - iframe.clientHeight));
|
||||
}
|
||||
|
||||
fullscreenState = !fullscreenState;
|
||||
|
||||
editorContainer = editor.getContainer();
|
||||
editorContainerStyle = editorContainer.style;
|
||||
iframe = editor.getContentAreaContainer().firstChild;
|
||||
iframeStyle = iframe.style;
|
||||
|
||||
if (fullscreenState) {
|
||||
iframeWidth = iframeStyle.width;
|
||||
iframeHeight = iframeStyle.height;
|
||||
iframeStyle.width = iframeStyle.height = '100%';
|
||||
containerWidth = editorContainerStyle.width;
|
||||
containerHeight = editorContainerStyle.height;
|
||||
editorContainerStyle.width = editorContainerStyle.height = '';
|
||||
|
||||
DOM.addClass(body, 'mce-fullscreen');
|
||||
DOM.addClass(documentElement, 'mce-fullscreen');
|
||||
DOM.addClass(editorContainer, 'mce-fullscreen');
|
||||
|
||||
DOM.bind(window, 'resize', resize);
|
||||
resize();
|
||||
resizeHandler = resize;
|
||||
} else {
|
||||
iframeStyle.width = iframeWidth;
|
||||
iframeStyle.height = iframeHeight;
|
||||
|
||||
if (containerWidth) {
|
||||
editorContainerStyle.width = containerWidth;
|
||||
}
|
||||
|
||||
if (containerHeight) {
|
||||
editorContainerStyle.height = containerHeight;
|
||||
}
|
||||
|
||||
DOM.removeClass(body, 'mce-fullscreen');
|
||||
DOM.removeClass(documentElement, 'mce-fullscreen');
|
||||
DOM.removeClass(editorContainer, 'mce-fullscreen');
|
||||
DOM.unbind(window, 'resize', resizeHandler);
|
||||
}
|
||||
|
||||
editor.fire('FullscreenStateChanged', {state: fullscreenState});
|
||||
}
|
||||
|
||||
editor.on('init', function() {
|
||||
editor.addShortcut('Ctrl+Alt+F', '', toggleFullscreen);
|
||||
});
|
||||
|
||||
editor.on('remove', function() {
|
||||
if (resizeHandler) {
|
||||
DOM.unbind(window, 'resize', resizeHandler);
|
||||
}
|
||||
});
|
||||
|
||||
editor.addCommand('mceFullScreen', toggleFullscreen);
|
||||
|
||||
editor.addMenuItem('fullscreen', {
|
||||
text: 'Fullscreen',
|
||||
shortcut: 'Ctrl+Alt+F',
|
||||
selectable: true,
|
||||
onClick: toggleFullscreen,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
editor.on('FullscreenStateChanged', function(e) {
|
||||
self.active(e.state);
|
||||
});
|
||||
},
|
||||
context: 'view'
|
||||
});
|
||||
|
||||
editor.addButton('fullscreen', {
|
||||
tooltip: 'Fullscreen',
|
||||
shortcut: 'Ctrl+Alt+F',
|
||||
onClick: toggleFullscreen,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
editor.on('FullscreenStateChanged', function(e) {
|
||||
self.active(e.state);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
isFullscreen: function() {
|
||||
return fullscreenState;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("fullscreen",function(a){function b(){var a,b,c=window,d=document,e=d.body;return e.offsetWidth&&(a=e.offsetWidth,b=e.offsetHeight),c.innerWidth&&c.innerHeight&&(a=c.innerWidth,b=c.innerHeight),{w:a,h:b}}function c(){function c(){j.setStyle(m,"height",b().h-(l.clientHeight-m.clientHeight))}var k,l,m,n,o=document.body,p=document.documentElement;i=!i,l=a.getContainer(),k=l.style,m=a.getContentAreaContainer().firstChild,n=m.style,i?(d=n.width,e=n.height,n.width=n.height="100%",g=k.width,h=k.height,k.width=k.height="",j.addClass(o,"mce-fullscreen"),j.addClass(p,"mce-fullscreen"),j.addClass(l,"mce-fullscreen"),j.bind(window,"resize",c),c(),f=c):(n.width=d,n.height=e,g&&(k.width=g),h&&(k.height=h),j.removeClass(o,"mce-fullscreen"),j.removeClass(p,"mce-fullscreen"),j.removeClass(l,"mce-fullscreen"),j.unbind(window,"resize",f)),a.fire("FullscreenStateChanged",{state:i})}var d,e,f,g,h,i=!1,j=tinymce.DOM;return a.settings.inline?void 0:(a.on("init",function(){a.addShortcut("Ctrl+Alt+F","",c)}),a.on("remove",function(){f&&j.unbind(window,"resize",f)}),a.addCommand("mceFullScreen",c),a.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Alt+F",selectable:!0,onClick:c,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})},context:"view"}),a.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Alt+F",onClick:c,onPostRender:function(){var b=this;a.on("FullscreenStateChanged",function(a){b.active(a.state)})}}),{isFullscreen:function(){return i}})});
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('hr', function(editor) {
|
||||
editor.addCommand('InsertHorizontalRule', function() {
|
||||
editor.execCommand('mceInsertContent', false, '<hr />');
|
||||
});
|
||||
|
||||
editor.addButton('hr', {
|
||||
icon: 'hr',
|
||||
tooltip: 'Horizontal line',
|
||||
cmd: 'InsertHorizontalRule'
|
||||
});
|
||||
|
||||
editor.addMenuItem('hr', {
|
||||
icon: 'hr',
|
||||
text: 'Horizontal line',
|
||||
cmd: 'InsertHorizontalRule',
|
||||
context: 'insert'
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("hr",function(a){a.addCommand("InsertHorizontalRule",function(){a.execCommand("mceInsertContent",!1,"<hr />")}),a.addButton("hr",{icon:"hr",tooltip:"Horizontal line",cmd:"InsertHorizontalRule"}),a.addMenuItem("hr",{icon:"hr",text:"Horizontal line",cmd:"InsertHorizontalRule",context:"insert"})});
|
|
@ -0,0 +1,439 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('image', function(editor) {
|
||||
function getImageSize(url, callback) {
|
||||
var img = document.createElement('img');
|
||||
|
||||
function done(width, height) {
|
||||
if (img.parentNode) {
|
||||
img.parentNode.removeChild(img);
|
||||
}
|
||||
|
||||
callback({width: width, height: height});
|
||||
}
|
||||
|
||||
img.onload = function() {
|
||||
done(img.clientWidth, img.clientHeight);
|
||||
};
|
||||
|
||||
img.onerror = function() {
|
||||
done();
|
||||
};
|
||||
|
||||
var style = img.style;
|
||||
style.visibility = 'hidden';
|
||||
style.position = 'fixed';
|
||||
style.bottom = style.left = 0;
|
||||
style.width = style.height = 'auto';
|
||||
|
||||
document.body.appendChild(img);
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
function buildListItems(inputList, itemCallback, startItems) {
|
||||
function appendItems(values, output) {
|
||||
output = output || [];
|
||||
|
||||
tinymce.each(values, function(item) {
|
||||
var menuItem = {text: item.text || item.title};
|
||||
|
||||
if (item.menu) {
|
||||
menuItem.menu = appendItems(item.menu);
|
||||
} else {
|
||||
menuItem.value = item.value;
|
||||
itemCallback(menuItem);
|
||||
}
|
||||
|
||||
output.push(menuItem);
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return appendItems(inputList, startItems || []);
|
||||
}
|
||||
|
||||
function createImageList(callback) {
|
||||
return function() {
|
||||
var imageList = editor.settings.image_list;
|
||||
|
||||
if (typeof(imageList) == "string") {
|
||||
tinymce.util.XHR.send({
|
||||
url: imageList,
|
||||
success: function(text) {
|
||||
callback(tinymce.util.JSON.parse(text));
|
||||
}
|
||||
});
|
||||
} else if (typeof(imageList) == "function") {
|
||||
imageList(callback);
|
||||
} else {
|
||||
callback(imageList);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function showDialog(imageList) {
|
||||
var win, data = {}, dom = editor.dom, imgElm = editor.selection.getNode();
|
||||
var width, height, imageListCtrl, classListCtrl, imageDimensions = editor.settings.image_dimensions !== false;
|
||||
|
||||
function recalcSize() {
|
||||
var widthCtrl, heightCtrl, newWidth, newHeight;
|
||||
|
||||
widthCtrl = win.find('#width')[0];
|
||||
heightCtrl = win.find('#height')[0];
|
||||
|
||||
if (!widthCtrl || !heightCtrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
newWidth = widthCtrl.value();
|
||||
newHeight = heightCtrl.value();
|
||||
|
||||
if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
|
||||
if (width != newWidth) {
|
||||
newHeight = Math.round((newWidth / width) * newHeight);
|
||||
heightCtrl.value(newHeight);
|
||||
} else {
|
||||
newWidth = Math.round((newHeight / height) * newWidth);
|
||||
widthCtrl.value(newWidth);
|
||||
}
|
||||
}
|
||||
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
}
|
||||
|
||||
function onSubmitForm() {
|
||||
function waitLoad(imgElm) {
|
||||
function selectImage() {
|
||||
imgElm.onload = imgElm.onerror = null;
|
||||
|
||||
if (editor.selection) {
|
||||
editor.selection.select(imgElm);
|
||||
editor.nodeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
imgElm.onload = function() {
|
||||
if (!data.width && !data.height && imageDimensions) {
|
||||
dom.setAttribs(imgElm, {
|
||||
width: imgElm.clientWidth,
|
||||
height: imgElm.clientHeight
|
||||
});
|
||||
}
|
||||
|
||||
selectImage();
|
||||
};
|
||||
|
||||
imgElm.onerror = selectImage;
|
||||
}
|
||||
|
||||
updateStyle();
|
||||
recalcSize();
|
||||
|
||||
data = tinymce.extend(data, win.toJSON());
|
||||
|
||||
if (!data.alt) {
|
||||
data.alt = '';
|
||||
}
|
||||
|
||||
if (data.width === '') {
|
||||
data.width = null;
|
||||
}
|
||||
|
||||
if (data.height === '') {
|
||||
data.height = null;
|
||||
}
|
||||
|
||||
if (!data.style) {
|
||||
data.style = null;
|
||||
}
|
||||
|
||||
// Setup new data excluding style properties
|
||||
data = {
|
||||
src: data.src,
|
||||
alt: data.alt,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
style: data.style,
|
||||
"class": data["class"]
|
||||
};
|
||||
|
||||
editor.undoManager.transact(function() {
|
||||
if (!data.src) {
|
||||
if (imgElm) {
|
||||
dom.remove(imgElm);
|
||||
editor.focus();
|
||||
editor.nodeChanged();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imgElm) {
|
||||
data.id = '__mcenew';
|
||||
editor.focus();
|
||||
editor.selection.setContent(dom.createHTML('img', data));
|
||||
imgElm = dom.get('__mcenew');
|
||||
dom.setAttrib(imgElm, 'id', null);
|
||||
} else {
|
||||
dom.setAttribs(imgElm, data);
|
||||
}
|
||||
|
||||
waitLoad(imgElm);
|
||||
});
|
||||
}
|
||||
|
||||
function removePixelSuffix(value) {
|
||||
if (value) {
|
||||
value = value.replace(/px$/, '');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function srcChange(e) {
|
||||
var meta = e.meta || {};
|
||||
|
||||
if (imageListCtrl) {
|
||||
imageListCtrl.value(editor.convertURL(this.value(), 'src'));
|
||||
}
|
||||
|
||||
tinymce.each(meta, function(value, key) {
|
||||
win.find('#' + key).value(value);
|
||||
});
|
||||
|
||||
if (!meta.width && !meta.height) {
|
||||
var srcURL = this.value(),
|
||||
absoluteURLPattern = new RegExp('^(?:[a-z]+:)?//', 'i'),
|
||||
baseURL = editor.settings.document_base_url;
|
||||
|
||||
//Pattern test the src url and make sure we haven't already prepended the url
|
||||
if (baseURL && !absoluteURLPattern.test(srcURL) && srcURL.substring(0, baseURL.length) !== baseURL) {
|
||||
this.value(baseURL + srcURL);
|
||||
}
|
||||
|
||||
getImageSize(this.value(), function(data) {
|
||||
if (data.width && data.height && imageDimensions) {
|
||||
width = data.width;
|
||||
height = data.height;
|
||||
|
||||
win.find('#width').value(width);
|
||||
win.find('#height').value(height);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
width = dom.getAttrib(imgElm, 'width');
|
||||
height = dom.getAttrib(imgElm, 'height');
|
||||
|
||||
if (imgElm.nodeName == 'IMG' && !imgElm.getAttribute('data-mce-object') && !imgElm.getAttribute('data-mce-placeholder')) {
|
||||
data = {
|
||||
src: dom.getAttrib(imgElm, 'src'),
|
||||
alt: dom.getAttrib(imgElm, 'alt'),
|
||||
"class": dom.getAttrib(imgElm, 'class'),
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
} else {
|
||||
imgElm = null;
|
||||
}
|
||||
|
||||
if (imageList) {
|
||||
imageListCtrl = {
|
||||
type: 'listbox',
|
||||
label: 'Image list',
|
||||
values: buildListItems(
|
||||
imageList,
|
||||
function(item) {
|
||||
item.value = editor.convertURL(item.value || item.url, 'src');
|
||||
},
|
||||
[{text: 'None', value: ''}]
|
||||
),
|
||||
value: data.src && editor.convertURL(data.src, 'src'),
|
||||
onselect: function(e) {
|
||||
var altCtrl = win.find('#alt');
|
||||
|
||||
if (!altCtrl.value() || (e.lastControl && altCtrl.value() == e.lastControl.text())) {
|
||||
altCtrl.value(e.control.text());
|
||||
}
|
||||
|
||||
win.find('#src').value(e.control.value()).fire('change');
|
||||
},
|
||||
onPostRender: function() {
|
||||
imageListCtrl = this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (editor.settings.image_class_list) {
|
||||
classListCtrl = {
|
||||
name: 'class',
|
||||
type: 'listbox',
|
||||
label: 'Class',
|
||||
values: buildListItems(
|
||||
editor.settings.image_class_list,
|
||||
function(item) {
|
||||
if (item.value) {
|
||||
item.textStyle = function() {
|
||||
return editor.formatter.getCssText({inline: 'img', classes: [item.value]});
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
// General settings shared between simple and advanced dialogs
|
||||
var generalFormItems = [
|
||||
{
|
||||
name: 'src',
|
||||
type: 'filepicker',
|
||||
filetype: 'image',
|
||||
label: 'Source',
|
||||
autofocus: true,
|
||||
onchange: srcChange
|
||||
},
|
||||
imageListCtrl
|
||||
];
|
||||
|
||||
if (editor.settings.image_description !== false) {
|
||||
generalFormItems.push({name: 'alt', type: 'textbox', label: 'Image description'});
|
||||
}
|
||||
|
||||
if (imageDimensions) {
|
||||
generalFormItems.push({
|
||||
type: 'container',
|
||||
label: 'Dimensions',
|
||||
layout: 'flex',
|
||||
direction: 'row',
|
||||
align: 'center',
|
||||
spacing: 5,
|
||||
items: [
|
||||
{name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'},
|
||||
{type: 'label', text: 'x'},
|
||||
{name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'},
|
||||
{name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
generalFormItems.push(classListCtrl);
|
||||
|
||||
function updateStyle() {
|
||||
function addPixelSuffix(value) {
|
||||
if (value.length > 0 && /^[0-9]+$/.test(value)) {
|
||||
value += 'px';
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
if (!editor.settings.image_advtab) {
|
||||
return;
|
||||
}
|
||||
|
||||
var data = win.toJSON();
|
||||
var css = dom.parseStyle(data.style);
|
||||
|
||||
delete css.margin;
|
||||
css['margin-top'] = css['margin-bottom'] = addPixelSuffix(data.vspace);
|
||||
css['margin-left'] = css['margin-right'] = addPixelSuffix(data.hspace);
|
||||
css['border-width'] = addPixelSuffix(data.border);
|
||||
|
||||
win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
|
||||
}
|
||||
|
||||
if (editor.settings.image_advtab) {
|
||||
// Parse styles from img
|
||||
if (imgElm) {
|
||||
data.hspace = removePixelSuffix(imgElm.style.marginLeft || imgElm.style.marginRight);
|
||||
data.vspace = removePixelSuffix(imgElm.style.marginTop || imgElm.style.marginBottom);
|
||||
data.border = removePixelSuffix(imgElm.style.borderWidth);
|
||||
data.style = editor.dom.serializeStyle(editor.dom.parseStyle(editor.dom.getAttrib(imgElm, 'style')));
|
||||
}
|
||||
|
||||
// Advanced dialog shows general+advanced tabs
|
||||
win = editor.windowManager.open({
|
||||
title: 'Insert/edit image',
|
||||
data: data,
|
||||
bodyType: 'tabpanel',
|
||||
body: [
|
||||
{
|
||||
title: 'General',
|
||||
type: 'form',
|
||||
items: generalFormItems
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Advanced',
|
||||
type: 'form',
|
||||
pack: 'start',
|
||||
items: [
|
||||
{
|
||||
label: 'Style',
|
||||
name: 'style',
|
||||
type: 'textbox'
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
layout: 'grid',
|
||||
packV: 'start',
|
||||
columns: 2,
|
||||
padding: 0,
|
||||
alignH: ['left', 'right'],
|
||||
defaults: {
|
||||
type: 'textbox',
|
||||
maxWidth: 50,
|
||||
onchange: updateStyle
|
||||
},
|
||||
items: [
|
||||
{label: 'Vertical space', name: 'vspace'},
|
||||
{label: 'Horizontal space', name: 'hspace'},
|
||||
{label: 'Border', name: 'border'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
onSubmit: onSubmitForm
|
||||
});
|
||||
} else {
|
||||
// Simple default dialog
|
||||
win = editor.windowManager.open({
|
||||
title: 'Insert/edit image',
|
||||
data: data,
|
||||
body: generalFormItems,
|
||||
onSubmit: onSubmitForm
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
editor.addButton('image', {
|
||||
icon: 'image',
|
||||
tooltip: 'Insert/edit image',
|
||||
onclick: createImageList(showDialog),
|
||||
stateSelector: 'img:not([data-mce-object],[data-mce-placeholder])'
|
||||
});
|
||||
|
||||
editor.addMenuItem('image', {
|
||||
icon: 'image',
|
||||
text: 'Insert image',
|
||||
onclick: createImageList(showDialog),
|
||||
context: 'insert',
|
||||
prependToContext: true
|
||||
});
|
||||
|
||||
editor.addCommand('mceImage', createImageList(showDialog));
|
||||
});
|
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('importcss', function(editor) {
|
||||
var self = this, each = tinymce.each;
|
||||
|
||||
function compileFilter(filter) {
|
||||
if (typeof(filter) == "string") {
|
||||
return function(value) {
|
||||
return value.indexOf(filter) !== -1;
|
||||
};
|
||||
} else if (filter instanceof RegExp) {
|
||||
return function(value) {
|
||||
return filter.test(value);
|
||||
};
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
function getSelectors(doc, fileFilter) {
|
||||
var selectors = [], contentCSSUrls = {};
|
||||
|
||||
function append(styleSheet, imported) {
|
||||
var href = styleSheet.href, rules;
|
||||
|
||||
if (!href || !fileFilter(href, imported)) {
|
||||
return;
|
||||
}
|
||||
|
||||
each(styleSheet.imports, function(styleSheet) {
|
||||
append(styleSheet, true);
|
||||
});
|
||||
|
||||
try {
|
||||
rules = styleSheet.cssRules || styleSheet.rules;
|
||||
} catch (e) {
|
||||
// Firefox fails on rules to remote domain for example:
|
||||
// @import url(//fonts.googleapis.com/css?family=Pathway+Gothic+One);
|
||||
}
|
||||
|
||||
each(rules, function(cssRule) {
|
||||
if (cssRule.styleSheet) {
|
||||
append(cssRule.styleSheet, true);
|
||||
} else if (cssRule.selectorText) {
|
||||
each(cssRule.selectorText.split(','), function(selector) {
|
||||
selectors.push(tinymce.trim(selector));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
each(editor.contentCSS, function(url) {
|
||||
contentCSSUrls[url] = true;
|
||||
});
|
||||
|
||||
if (!fileFilter) {
|
||||
fileFilter = function(href, imported) {
|
||||
return imported || contentCSSUrls[href];
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
each(doc.styleSheets, function(styleSheet) {
|
||||
append(styleSheet);
|
||||
});
|
||||
} catch (e) {}
|
||||
|
||||
return selectors;
|
||||
}
|
||||
|
||||
function convertSelectorToFormat(selectorText) {
|
||||
var format;
|
||||
|
||||
// Parse simple element.class1, .class1
|
||||
var selector = /^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(selectorText);
|
||||
if (!selector) {
|
||||
return;
|
||||
}
|
||||
|
||||
var elementName = selector[1];
|
||||
var classes = selector[2].substr(1).split('.').join(' ');
|
||||
var inlineSelectorElements = tinymce.makeMap('a,img');
|
||||
|
||||
// element.class - Produce block formats
|
||||
if (selector[1]) {
|
||||
format = {
|
||||
title: selectorText
|
||||
};
|
||||
|
||||
if (editor.schema.getTextBlockElements()[elementName]) {
|
||||
// Text block format ex: h1.class1
|
||||
format.block = elementName;
|
||||
} else if (editor.schema.getBlockElements()[elementName] || inlineSelectorElements[elementName.toLowerCase()]) {
|
||||
// Block elements such as table.class and special inline elements such as a.class or img.class
|
||||
format.selector = elementName;
|
||||
} else {
|
||||
// Inline format strong.class1
|
||||
format.inline = elementName;
|
||||
}
|
||||
} else if (selector[2]) {
|
||||
// .class - Produce inline span with classes
|
||||
format = {
|
||||
inline: 'span',
|
||||
title: selectorText.substr(1),
|
||||
classes: classes
|
||||
};
|
||||
}
|
||||
|
||||
// Append to or override class attribute
|
||||
if (editor.settings.importcss_merge_classes !== false) {
|
||||
format.classes = classes;
|
||||
} else {
|
||||
format.attributes = {"class": classes};
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
editor.on('renderFormatsMenu', function(e) {
|
||||
var settings = editor.settings, selectors = {};
|
||||
var selectorConverter = settings.importcss_selector_converter || convertSelectorToFormat;
|
||||
var selectorFilter = compileFilter(settings.importcss_selector_filter), ctrl = e.control;
|
||||
|
||||
if (!editor.settings.importcss_append) {
|
||||
ctrl.items().remove();
|
||||
}
|
||||
|
||||
// Setup new groups collection by cloning the configured one
|
||||
var groups = [];
|
||||
tinymce.each(settings.importcss_groups, function(group) {
|
||||
group = tinymce.extend({}, group);
|
||||
group.filter = compileFilter(group.filter);
|
||||
groups.push(group);
|
||||
});
|
||||
|
||||
each(getSelectors(e.doc || editor.getDoc(), compileFilter(settings.importcss_file_filter)), function(selector) {
|
||||
if (selector.indexOf('.mce-') === -1) {
|
||||
if (!selectors[selector] && (!selectorFilter || selectorFilter(selector))) {
|
||||
var format = selectorConverter.call(self, selector), menu;
|
||||
|
||||
if (format) {
|
||||
var formatName = format.name || tinymce.DOM.uniqueId();
|
||||
|
||||
if (groups) {
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
if (!groups[i].filter || groups[i].filter(selector)) {
|
||||
if (!groups[i].item) {
|
||||
groups[i].item = {text: groups[i].title, menu: []};
|
||||
}
|
||||
|
||||
menu = groups[i].item.menu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.formatter.register(formatName, format);
|
||||
|
||||
var menuItem = tinymce.extend({}, ctrl.settings.itemDefaults, {
|
||||
text: format.title,
|
||||
format: formatName
|
||||
});
|
||||
|
||||
if (menu) {
|
||||
menu.push(menuItem);
|
||||
} else {
|
||||
ctrl.add(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
selectors[selector] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
each(groups, function(group) {
|
||||
ctrl.add(group.item);
|
||||
});
|
||||
|
||||
e.control.renderNew();
|
||||
});
|
||||
|
||||
// Expose default convertSelectorToFormat implementation
|
||||
self.convertSelectorToFormat = convertSelectorToFormat;
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("importcss",function(a){function b(a){return"string"==typeof a?function(b){return-1!==b.indexOf(a)}:a instanceof RegExp?function(b){return a.test(b)}:a}function c(b,c){function d(a,b){var g,h=a.href;if(h&&c(h,b)){f(a.imports,function(a){d(a,!0)});try{g=a.cssRules||a.rules}catch(i){}f(g,function(a){a.styleSheet?d(a.styleSheet,!0):a.selectorText&&f(a.selectorText.split(","),function(a){e.push(tinymce.trim(a))})})}}var e=[],g={};f(a.contentCSS,function(a){g[a]=!0}),c||(c=function(a,b){return b||g[a]});try{f(b.styleSheets,function(a){d(a)})}catch(h){}return e}function d(b){var c,d=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(b);if(d){var e=d[1],f=d[2].substr(1).split(".").join(" "),g=tinymce.makeMap("a,img");return d[1]?(c={title:b},a.schema.getTextBlockElements()[e]?c.block=e:a.schema.getBlockElements()[e]||g[e.toLowerCase()]?c.selector=e:c.inline=e):d[2]&&(c={inline:"span",title:b.substr(1),classes:f}),a.settings.importcss_merge_classes!==!1?c.classes=f:c.attributes={"class":f},c}}var e=this,f=tinymce.each;a.on("renderFormatsMenu",function(g){var h=a.settings,i={},j=h.importcss_selector_converter||d,k=b(h.importcss_selector_filter),l=g.control;a.settings.importcss_append||l.items().remove();var m=[];tinymce.each(h.importcss_groups,function(a){a=tinymce.extend({},a),a.filter=b(a.filter),m.push(a)}),f(c(g.doc||a.getDoc(),b(h.importcss_file_filter)),function(b){if(-1===b.indexOf(".mce-")&&!i[b]&&(!k||k(b))){var c,d=j.call(e,b);if(d){var f=d.name||tinymce.DOM.uniqueId();if(m)for(var g=0;g<m.length;g++)if(!m[g].filter||m[g].filter(b)){m[g].item||(m[g].item={text:m[g].title,menu:[]}),c=m[g].item.menu;break}a.formatter.register(f,d);var h=tinymce.extend({},l.settings.itemDefaults,{text:d.title,format:f});c?c.push(h):l.add(h)}i[b]=!0}}),f(m,function(a){l.add(a.item)}),g.control.renderNew()}),e.convertSelectorToFormat=d});
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('insertdatetime', function(editor) {
|
||||
var daysShort = "Sun Mon Tue Wed Thu Fri Sat Sun".split(' ');
|
||||
var daysLong = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(' ');
|
||||
var monthsShort = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(' ');
|
||||
var monthsLong = "January February March April May June July August September October November December".split(' ');
|
||||
var menuItems = [], lastFormat, defaultButtonTimeFormat;
|
||||
|
||||
function getDateTime(fmt, date) {
|
||||
function addZeros(value, len) {
|
||||
value = "" + value;
|
||||
|
||||
if (value.length < len) {
|
||||
for (var i = 0; i < (len - value.length); i++) {
|
||||
value = "0" + value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
date = date || new Date();
|
||||
|
||||
fmt = fmt.replace("%D", "%m/%d/%Y");
|
||||
fmt = fmt.replace("%r", "%I:%M:%S %p");
|
||||
fmt = fmt.replace("%Y", "" + date.getFullYear());
|
||||
fmt = fmt.replace("%y", "" + date.getYear());
|
||||
fmt = fmt.replace("%m", addZeros(date.getMonth() + 1, 2));
|
||||
fmt = fmt.replace("%d", addZeros(date.getDate(), 2));
|
||||
fmt = fmt.replace("%H", "" + addZeros(date.getHours(), 2));
|
||||
fmt = fmt.replace("%M", "" + addZeros(date.getMinutes(), 2));
|
||||
fmt = fmt.replace("%S", "" + addZeros(date.getSeconds(), 2));
|
||||
fmt = fmt.replace("%I", "" + ((date.getHours() + 11) % 12 + 1));
|
||||
fmt = fmt.replace("%p", "" + (date.getHours() < 12 ? "AM" : "PM"));
|
||||
fmt = fmt.replace("%B", "" + editor.translate(monthsLong[date.getMonth()]));
|
||||
fmt = fmt.replace("%b", "" + editor.translate(monthsShort[date.getMonth()]));
|
||||
fmt = fmt.replace("%A", "" + editor.translate(daysLong[date.getDay()]));
|
||||
fmt = fmt.replace("%a", "" + editor.translate(daysShort[date.getDay()]));
|
||||
fmt = fmt.replace("%%", "%");
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
function insertDateTime(format) {
|
||||
var html = getDateTime(format);
|
||||
|
||||
if (editor.settings.insertdatetime_element) {
|
||||
var computerTime;
|
||||
|
||||
if (/%[HMSIp]/.test(format)) {
|
||||
computerTime = getDateTime("%Y-%m-%dT%H:%M");
|
||||
} else {
|
||||
computerTime = getDateTime("%Y-%m-%d");
|
||||
}
|
||||
|
||||
html = '<time datetime="' + computerTime + '">' + html + '</time>';
|
||||
|
||||
var timeElm = editor.dom.getParent(editor.selection.getStart(), 'time');
|
||||
if (timeElm) {
|
||||
editor.dom.setOuterHTML(timeElm, html);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
editor.insertContent(html);
|
||||
}
|
||||
|
||||
editor.addCommand('mceInsertDate', function() {
|
||||
insertDateTime(editor.getParam("insertdatetime_dateformat", editor.translate("%Y-%m-%d")));
|
||||
});
|
||||
|
||||
editor.addCommand('mceInsertTime', function() {
|
||||
insertDateTime(editor.getParam("insertdatetime_timeformat", editor.translate('%H:%M:%S')));
|
||||
});
|
||||
|
||||
editor.addButton('insertdatetime', {
|
||||
type: 'splitbutton',
|
||||
title: 'Insert date/time',
|
||||
onclick: function() {
|
||||
insertDateTime(lastFormat || defaultButtonTimeFormat);
|
||||
},
|
||||
menu: menuItems
|
||||
});
|
||||
|
||||
tinymce.each(editor.settings.insertdatetime_formats || [
|
||||
"%H:%M:%S",
|
||||
"%Y-%m-%d",
|
||||
"%I:%M:%S %p",
|
||||
"%D"
|
||||
], function(fmt) {
|
||||
if (!defaultButtonTimeFormat) {
|
||||
defaultButtonTimeFormat = fmt;
|
||||
}
|
||||
|
||||
menuItems.push({
|
||||
text: getDateTime(fmt),
|
||||
onclick: function() {
|
||||
lastFormat = fmt;
|
||||
insertDateTime(fmt);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.addMenuItem('insertdatetime', {
|
||||
icon: 'date',
|
||||
text: 'Insert date/time',
|
||||
menu: menuItems,
|
||||
context: 'insert'
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("insertdatetime",function(a){function b(b,c){function d(a,b){if(a=""+a,a.length<b)for(var c=0;c<b-a.length;c++)a="0"+a;return a}return c=c||new Date,b=b.replace("%D","%m/%d/%Y"),b=b.replace("%r","%I:%M:%S %p"),b=b.replace("%Y",""+c.getFullYear()),b=b.replace("%y",""+c.getYear()),b=b.replace("%m",d(c.getMonth()+1,2)),b=b.replace("%d",d(c.getDate(),2)),b=b.replace("%H",""+d(c.getHours(),2)),b=b.replace("%M",""+d(c.getMinutes(),2)),b=b.replace("%S",""+d(c.getSeconds(),2)),b=b.replace("%I",""+((c.getHours()+11)%12+1)),b=b.replace("%p",""+(c.getHours()<12?"AM":"PM")),b=b.replace("%B",""+a.translate(i[c.getMonth()])),b=b.replace("%b",""+a.translate(h[c.getMonth()])),b=b.replace("%A",""+a.translate(g[c.getDay()])),b=b.replace("%a",""+a.translate(f[c.getDay()])),b=b.replace("%%","%")}function c(c){var d=b(c);if(a.settings.insertdatetime_element){var e;e=b(/%[HMSIp]/.test(c)?"%Y-%m-%dT%H:%M":"%Y-%m-%d"),d='<time datetime="'+e+'">'+d+"</time>";var f=a.dom.getParent(a.selection.getStart(),"time");if(f)return void a.dom.setOuterHTML(f,d)}a.insertContent(d)}var d,e,f="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),g="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),h="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),i="January February March April May June July August September October November December".split(" "),j=[];a.addCommand("mceInsertDate",function(){c(a.getParam("insertdatetime_dateformat",a.translate("%Y-%m-%d")))}),a.addCommand("mceInsertTime",function(){c(a.getParam("insertdatetime_timeformat",a.translate("%H:%M:%S")))}),a.addButton("insertdatetime",{type:"splitbutton",title:"Insert date/time",onclick:function(){c(d||e)},menu:j}),tinymce.each(a.settings.insertdatetime_formats||["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"],function(a){e||(e=a),j.push({text:b(a),onclick:function(){d=a,c(a)}})}),a.addMenuItem("insertdatetime",{icon:"date",text:"Insert date/time",menu:j,context:"insert"})});
|
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('layer', function(editor) {
|
||||
function getParentLayer(node) {
|
||||
do {
|
||||
if (node.className && node.className.indexOf('mceItemLayer') != -1) {
|
||||
return node;
|
||||
}
|
||||
} while ((node = node.parentNode));
|
||||
}
|
||||
|
||||
function visualAid(e) {
|
||||
var dom = editor.dom;
|
||||
|
||||
tinymce.each(dom.select('div,p', e), function(e) {
|
||||
if (/^(absolute|relative|fixed)$/i.test(e.style.position)) {
|
||||
if (e.hasVisual) {
|
||||
dom.addClass(e, 'mceItemVisualAid');
|
||||
} else {
|
||||
dom.removeClass(e, 'mceItemVisualAid');
|
||||
}
|
||||
|
||||
dom.addClass(e, 'mceItemLayer');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function move(d) {
|
||||
var i, z = [], le = getParentLayer(editor.selection.getNode()), ci = -1, fi = -1, nl;
|
||||
|
||||
nl = [];
|
||||
tinymce.walk(editor.getBody(), function(n) {
|
||||
if (n.nodeType == 1 && /^(absolute|relative|static)$/i.test(n.style.position)) {
|
||||
nl.push(n);
|
||||
}
|
||||
}, 'childNodes');
|
||||
|
||||
// Find z-indexes
|
||||
for (i = 0; i < nl.length; i++) {
|
||||
z[i] = nl[i].style.zIndex ? parseInt(nl[i].style.zIndex, 10) : 0;
|
||||
|
||||
if (ci < 0 && nl[i] == le) {
|
||||
ci = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < 0) {
|
||||
// Move back
|
||||
|
||||
// Try find a lower one
|
||||
for (i = 0; i < z.length; i++) {
|
||||
if (z[i] < z[ci]) {
|
||||
fi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fi > -1) {
|
||||
nl[ci].style.zIndex = z[fi];
|
||||
nl[fi].style.zIndex = z[ci];
|
||||
} else {
|
||||
if (z[ci] > 0) {
|
||||
nl[ci].style.zIndex = z[ci] - 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Move forward
|
||||
|
||||
// Try find a higher one
|
||||
for (i = 0; i < z.length; i++) {
|
||||
if (z[i] > z[ci]) {
|
||||
fi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fi > -1) {
|
||||
nl[ci].style.zIndex = z[fi];
|
||||
nl[fi].style.zIndex = z[ci];
|
||||
} else {
|
||||
nl[ci].style.zIndex = z[ci] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
editor.execCommand('mceRepaint');
|
||||
}
|
||||
|
||||
function insertLayer() {
|
||||
var dom = editor.dom, p = dom.getPos(dom.getParent(editor.selection.getNode(), '*'));
|
||||
var body = editor.getBody();
|
||||
|
||||
editor.dom.add(body, 'div', {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: p.x,
|
||||
top: (p.y > 20 ? p.y : 20),
|
||||
width: 100,
|
||||
height: 100
|
||||
},
|
||||
'class': 'mceItemVisualAid mceItemLayer'
|
||||
}, editor.selection.getContent() || editor.getLang('layer.content'));
|
||||
|
||||
// Workaround for IE where it messes up the JS engine if you insert a layer on IE 6,7
|
||||
if (tinymce.Env.ie) {
|
||||
dom.setHTML(body, body.innerHTML);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAbsolute() {
|
||||
var le = getParentLayer(editor.selection.getNode());
|
||||
|
||||
if (!le) {
|
||||
le = editor.dom.getParent(editor.selection.getNode(), 'DIV,P,IMG');
|
||||
}
|
||||
|
||||
if (le) {
|
||||
if (le.style.position.toLowerCase() == "absolute") {
|
||||
editor.dom.setStyles(le, {
|
||||
position: '',
|
||||
left: '',
|
||||
top: '',
|
||||
width: '',
|
||||
height: ''
|
||||
});
|
||||
|
||||
editor.dom.removeClass(le, 'mceItemVisualAid');
|
||||
editor.dom.removeClass(le, 'mceItemLayer');
|
||||
} else {
|
||||
if (!le.style.left) {
|
||||
le.style.left = 20 + 'px';
|
||||
}
|
||||
|
||||
if (!le.style.top) {
|
||||
le.style.top = 20 + 'px';
|
||||
}
|
||||
|
||||
if (!le.style.width) {
|
||||
le.style.width = le.width ? (le.width + 'px') : '100px';
|
||||
}
|
||||
|
||||
if (!le.style.height) {
|
||||
le.style.height = le.height ? (le.height + 'px') : '100px';
|
||||
}
|
||||
|
||||
le.style.position = "absolute";
|
||||
|
||||
editor.dom.setAttrib(le, 'data-mce-style', '');
|
||||
editor.addVisual(editor.getBody());
|
||||
}
|
||||
|
||||
editor.execCommand('mceRepaint');
|
||||
editor.nodeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// Register commands
|
||||
editor.addCommand('mceInsertLayer', insertLayer);
|
||||
|
||||
editor.addCommand('mceMoveForward', function() {
|
||||
move(1);
|
||||
});
|
||||
|
||||
editor.addCommand('mceMoveBackward', function() {
|
||||
move(-1);
|
||||
});
|
||||
|
||||
editor.addCommand('mceMakeAbsolute', function() {
|
||||
toggleAbsolute();
|
||||
});
|
||||
|
||||
// Register buttons
|
||||
editor.addButton('moveforward', {title: 'layer.forward_desc', cmd: 'mceMoveForward'});
|
||||
editor.addButton('movebackward', {title: 'layer.backward_desc', cmd: 'mceMoveBackward'});
|
||||
editor.addButton('absolute', {title: 'layer.absolute_desc', cmd: 'mceMakeAbsolute'});
|
||||
editor.addButton('insertlayer', {title: 'layer.insertlayer_desc', cmd: 'mceInsertLayer'});
|
||||
|
||||
editor.on('init', function() {
|
||||
if (tinymce.Env.ie) {
|
||||
editor.getDoc().execCommand('2D-Position', false, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove serialized styles when selecting a layer since it might be changed by a drag operation
|
||||
editor.on('mouseup', function(e) {
|
||||
var layer = getParentLayer(e.target);
|
||||
|
||||
if (layer) {
|
||||
editor.dom.setAttrib(layer, 'data-mce-style', '');
|
||||
}
|
||||
});
|
||||
|
||||
// Fixes edit focus issues with layers on Gecko
|
||||
// This will enable designMode while inside a layer and disable it when outside
|
||||
editor.on('mousedown', function(e) {
|
||||
var node = e.target, doc = editor.getDoc(), parent;
|
||||
|
||||
if (tinymce.Env.gecko) {
|
||||
if (getParentLayer(node)) {
|
||||
if (doc.designMode !== 'on') {
|
||||
doc.designMode = 'on';
|
||||
|
||||
// Repaint caret
|
||||
node = doc.body;
|
||||
parent = node.parentNode;
|
||||
parent.removeChild(node);
|
||||
parent.appendChild(node);
|
||||
}
|
||||
} else if (doc.designMode == 'on') {
|
||||
doc.designMode = 'off';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('NodeChange', visualAid);
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("layer",function(a){function b(a){do if(a.className&&-1!=a.className.indexOf("mceItemLayer"))return a;while(a=a.parentNode)}function c(b){var c=a.dom;tinymce.each(c.select("div,p",b),function(a){/^(absolute|relative|fixed)$/i.test(a.style.position)&&(a.hasVisual?c.addClass(a,"mceItemVisualAid"):c.removeClass(a,"mceItemVisualAid"),c.addClass(a,"mceItemLayer"))})}function d(c){var d,e,f=[],g=b(a.selection.getNode()),h=-1,i=-1;for(e=[],tinymce.walk(a.getBody(),function(a){1==a.nodeType&&/^(absolute|relative|static)$/i.test(a.style.position)&&e.push(a)},"childNodes"),d=0;d<e.length;d++)f[d]=e[d].style.zIndex?parseInt(e[d].style.zIndex,10):0,0>h&&e[d]==g&&(h=d);if(0>c){for(d=0;d<f.length;d++)if(f[d]<f[h]){i=d;break}i>-1?(e[h].style.zIndex=f[i],e[i].style.zIndex=f[h]):f[h]>0&&(e[h].style.zIndex=f[h]-1)}else{for(d=0;d<f.length;d++)if(f[d]>f[h]){i=d;break}i>-1?(e[h].style.zIndex=f[i],e[i].style.zIndex=f[h]):e[h].style.zIndex=f[h]+1}a.execCommand("mceRepaint")}function e(){var b=a.dom,c=b.getPos(b.getParent(a.selection.getNode(),"*")),d=a.getBody();a.dom.add(d,"div",{style:{position:"absolute",left:c.x,top:c.y>20?c.y:20,width:100,height:100},"class":"mceItemVisualAid mceItemLayer"},a.selection.getContent()||a.getLang("layer.content")),tinymce.Env.ie&&b.setHTML(d,d.innerHTML)}function f(){var c=b(a.selection.getNode());c||(c=a.dom.getParent(a.selection.getNode(),"DIV,P,IMG")),c&&("absolute"==c.style.position.toLowerCase()?(a.dom.setStyles(c,{position:"",left:"",top:"",width:"",height:""}),a.dom.removeClass(c,"mceItemVisualAid"),a.dom.removeClass(c,"mceItemLayer")):(c.style.left||(c.style.left="20px"),c.style.top||(c.style.top="20px"),c.style.width||(c.style.width=c.width?c.width+"px":"100px"),c.style.height||(c.style.height=c.height?c.height+"px":"100px"),c.style.position="absolute",a.dom.setAttrib(c,"data-mce-style",""),a.addVisual(a.getBody())),a.execCommand("mceRepaint"),a.nodeChanged())}a.addCommand("mceInsertLayer",e),a.addCommand("mceMoveForward",function(){d(1)}),a.addCommand("mceMoveBackward",function(){d(-1)}),a.addCommand("mceMakeAbsolute",function(){f()}),a.addButton("moveforward",{title:"layer.forward_desc",cmd:"mceMoveForward"}),a.addButton("movebackward",{title:"layer.backward_desc",cmd:"mceMoveBackward"}),a.addButton("absolute",{title:"layer.absolute_desc",cmd:"mceMakeAbsolute"}),a.addButton("insertlayer",{title:"layer.insertlayer_desc",cmd:"mceInsertLayer"}),a.on("init",function(){tinymce.Env.ie&&a.getDoc().execCommand("2D-Position",!1,!0)}),a.on("mouseup",function(c){var d=b(c.target);d&&a.dom.setAttrib(d,"data-mce-style","")}),a.on("mousedown",function(c){var d,e=c.target,f=a.getDoc();tinymce.Env.gecko&&(b(e)?"on"!==f.designMode&&(f.designMode="on",e=f.body,d=e.parentNode,d.removeChild(e),d.appendChild(e)):"on"==f.designMode&&(f.designMode="off"))}),a.on("NodeChange",c)});
|
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*
|
||||
* This plugin will force TinyMCE to produce deprecated legacy output such as font elements, u elements, align
|
||||
* attributes and so forth. There are a few cases where these old items might be needed for example in email applications or with Flash
|
||||
*
|
||||
* However you should NOT use this plugin if you are building some system that produces web contents such as a CMS. All these elements are
|
||||
* not apart of the newer specifications for HTML and XHTML.
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
(function(tinymce) {
|
||||
// Override inline_styles setting to force TinyMCE to produce deprecated contents
|
||||
tinymce.on('AddEditor', function(e) {
|
||||
e.editor.settings.inline_styles = false;
|
||||
});
|
||||
|
||||
tinymce.PluginManager.add('legacyoutput', function(editor, url, $) {
|
||||
editor.on('init', function() {
|
||||
var alignElements = 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img',
|
||||
fontSizes = tinymce.explode(editor.settings.font_size_style_values),
|
||||
schema = editor.schema;
|
||||
|
||||
// Override some internal formats to produce legacy elements and attributes
|
||||
editor.formatter.register({
|
||||
// Change alignment formats to use the deprecated align attribute
|
||||
alignleft: {selector: alignElements, attributes: {align: 'left'}},
|
||||
aligncenter: {selector: alignElements, attributes: {align: 'center'}},
|
||||
alignright: {selector: alignElements, attributes: {align: 'right'}},
|
||||
alignjustify: {selector: alignElements, attributes: {align: 'justify'}},
|
||||
|
||||
// Change the basic formatting elements to use deprecated element types
|
||||
bold: [
|
||||
{inline: 'b', remove: 'all'},
|
||||
{inline: 'strong', remove: 'all'},
|
||||
{inline: 'span', styles: {fontWeight: 'bold'}}
|
||||
],
|
||||
italic: [
|
||||
{inline: 'i', remove: 'all'},
|
||||
{inline: 'em', remove: 'all'},
|
||||
{inline: 'span', styles: {fontStyle: 'italic'}}
|
||||
],
|
||||
underline: [
|
||||
{inline: 'u', remove: 'all'},
|
||||
{inline: 'span', styles: {textDecoration: 'underline'}, exact: true}
|
||||
],
|
||||
strikethrough: [
|
||||
{inline: 'strike', remove: 'all'},
|
||||
{inline: 'span', styles: {textDecoration: 'line-through'}, exact: true}
|
||||
],
|
||||
|
||||
// Change font size and font family to use the deprecated font element
|
||||
fontname: {inline: 'font', attributes: {face: '%value'}},
|
||||
fontsize: {
|
||||
inline: 'font',
|
||||
attributes: {
|
||||
size: function(vars) {
|
||||
return tinymce.inArray(fontSizes, vars.value) + 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Setup font elements for colors as well
|
||||
forecolor: {inline: 'font', attributes: {color: '%value'}},
|
||||
hilitecolor: {inline: 'font', styles: {backgroundColor: '%value'}}
|
||||
});
|
||||
|
||||
// Check that deprecated elements are allowed if not add them
|
||||
tinymce.each('b,i,u,strike'.split(','), function(name) {
|
||||
schema.addValidElements(name + '[*]');
|
||||
});
|
||||
|
||||
// Add font element if it's missing
|
||||
if (!schema.getElementRule("font")) {
|
||||
schema.addValidElements("font[face|size|color|style]");
|
||||
}
|
||||
|
||||
// Add the missing and depreacted align attribute for the serialization engine
|
||||
tinymce.each(alignElements.split(','), function(name) {
|
||||
var rule = schema.getElementRule(name);
|
||||
|
||||
if (rule) {
|
||||
if (!rule.attributes.align) {
|
||||
rule.attributes.align = {};
|
||||
rule.attributesOrder.push('align');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.addButton('fontsizeselect', function() {
|
||||
var items = [], defaultFontsizeFormats = '8pt=1 10pt=2 12pt=3 14pt=4 18pt=5 24pt=6 36pt=7';
|
||||
var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats;
|
||||
|
||||
editor.$.each(fontsize_formats.split(' '), function(i, item) {
|
||||
var text = item, value = item;
|
||||
var values = item.split('=');
|
||||
|
||||
if (values.length > 1) {
|
||||
text = values[0];
|
||||
value = values[1];
|
||||
}
|
||||
|
||||
items.push({text: text, value: value});
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'listbox',
|
||||
text: 'Font Sizes',
|
||||
tooltip: 'Font Sizes',
|
||||
values: items,
|
||||
fixedWidth: true,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
editor.on('NodeChange', function() {
|
||||
var fontElm;
|
||||
|
||||
fontElm = editor.dom.getParent(editor.selection.getNode(), 'font');
|
||||
if (fontElm) {
|
||||
self.value(fontElm.size);
|
||||
} else {
|
||||
self.value('');
|
||||
}
|
||||
});
|
||||
},
|
||||
onclick: function(e) {
|
||||
if (e.control.settings.value) {
|
||||
editor.execCommand('FontSize', false, e.control.settings.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
editor.addButton('fontselect', function() {
|
||||
function createFormats(formats) {
|
||||
formats = formats.replace(/;$/, '').split(';');
|
||||
|
||||
var i = formats.length;
|
||||
while (i--) {
|
||||
formats[i] = formats[i].split('=');
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
var defaultFontsFormats =
|
||||
'Andale Mono=andale mono,times;' +
|
||||
'Arial=arial,helvetica,sans-serif;' +
|
||||
'Arial Black=arial black,avant garde;' +
|
||||
'Book Antiqua=book antiqua,palatino;' +
|
||||
'Comic Sans MS=comic sans ms,sans-serif;' +
|
||||
'Courier New=courier new,courier;' +
|
||||
'Georgia=georgia,palatino;' +
|
||||
'Helvetica=helvetica;' +
|
||||
'Impact=impact,chicago;' +
|
||||
'Symbol=symbol;' +
|
||||
'Tahoma=tahoma,arial,helvetica,sans-serif;' +
|
||||
'Terminal=terminal,monaco;' +
|
||||
'Times New Roman=times new roman,times;' +
|
||||
'Trebuchet MS=trebuchet ms,geneva;' +
|
||||
'Verdana=verdana,geneva;' +
|
||||
'Webdings=webdings;' +
|
||||
'Wingdings=wingdings,zapf dingbats';
|
||||
|
||||
var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats);
|
||||
|
||||
$.each(fonts, function(i, font) {
|
||||
items.push({
|
||||
text: {raw: font[0]},
|
||||
value: font[1],
|
||||
textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : ''
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'listbox',
|
||||
text: 'Font Family',
|
||||
tooltip: 'Font Family',
|
||||
values: items,
|
||||
fixedWidth: true,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
editor.on('NodeChange', function() {
|
||||
var fontElm;
|
||||
|
||||
fontElm = editor.dom.getParent(editor.selection.getNode(), 'font');
|
||||
if (fontElm) {
|
||||
self.value(fontElm.face);
|
||||
} else {
|
||||
self.value('');
|
||||
}
|
||||
});
|
||||
},
|
||||
onselect: function(e) {
|
||||
if (e.control.settings.value) {
|
||||
editor.execCommand('FontName', false, e.control.settings.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
})(tinymce);
|
|
@ -0,0 +1 @@
|
|||
!function(a){a.on("AddEditor",function(a){a.editor.settings.inline_styles=!1}),a.PluginManager.add("legacyoutput",function(b,c,d){b.on("init",function(){var c="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",d=a.explode(b.settings.font_size_style_values),e=b.schema;b.formatter.register({alignleft:{selector:c,attributes:{align:"left"}},aligncenter:{selector:c,attributes:{align:"center"}},alignright:{selector:c,attributes:{align:"right"}},alignjustify:{selector:c,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:!0}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:!0}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(b){return a.inArray(d,b.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}}),a.each("b,i,u,strike".split(","),function(a){e.addValidElements(a+"[*]")}),e.getElementRule("font")||e.addValidElements("font[face|size|color|style]"),a.each(c.split(","),function(a){var b=e.getElementRule(a);b&&(b.attributes.align||(b.attributes.align={},b.attributesOrder.push("align")))})}),b.addButton("fontsizeselect",function(){var a=[],c="8pt=1 10pt=2 12pt=3 14pt=4 18pt=5 24pt=6 36pt=7",d=b.settings.fontsize_formats||c;return b.$.each(d.split(" "),function(b,c){var d=c,e=c,f=c.split("=");f.length>1&&(d=f[0],e=f[1]),a.push({text:d,value:e})}),{type:"listbox",text:"Font Sizes",tooltip:"Font Sizes",values:a,fixedWidth:!0,onPostRender:function(){var a=this;b.on("NodeChange",function(){var c;c=b.dom.getParent(b.selection.getNode(),"font"),a.value(c?c.size:"")})},onclick:function(a){a.control.settings.value&&b.execCommand("FontSize",!1,a.control.settings.value)}}}),b.addButton("fontselect",function(){function a(a){a=a.replace(/;$/,"").split(";");for(var b=a.length;b--;)a[b]=a[b].split("=");return a}var c="Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",e=[],f=a(b.settings.font_formats||c);return d.each(f,function(a,b){e.push({text:{raw:b[0]},value:b[1],textStyle:-1==b[1].indexOf("dings")?"font-family:"+b[1]:""})}),{type:"listbox",text:"Font Family",tooltip:"Font Family",values:e,fixedWidth:!0,onPostRender:function(){var a=this;b.on("NodeChange",function(){var c;c=b.dom.getParent(b.selection.getNode(),"font"),a.value(c?c.face:"")})},onselect:function(a){a.control.settings.value&&b.execCommand("FontName",!1,a.control.settings.value)}}})})}(tinymce);
|
|
@ -0,0 +1,400 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('link', function(editor) {
|
||||
function createLinkList(callback) {
|
||||
return function() {
|
||||
var linkList = editor.settings.link_list;
|
||||
|
||||
if (typeof(linkList) == "string") {
|
||||
tinymce.util.XHR.send({
|
||||
url: linkList,
|
||||
success: function(text) {
|
||||
callback(tinymce.util.JSON.parse(text));
|
||||
}
|
||||
});
|
||||
} else if (typeof(linkList) == "function") {
|
||||
linkList(callback);
|
||||
} else {
|
||||
callback(linkList);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function buildListItems(inputList, itemCallback, startItems) {
|
||||
function appendItems(values, output) {
|
||||
output = output || [];
|
||||
|
||||
tinymce.each(values, function(item) {
|
||||
var menuItem = {text: item.text || item.title};
|
||||
|
||||
if (item.menu) {
|
||||
menuItem.menu = appendItems(item.menu);
|
||||
} else {
|
||||
menuItem.value = item.value;
|
||||
|
||||
if (itemCallback) {
|
||||
itemCallback(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
output.push(menuItem);
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
return appendItems(inputList, startItems || []);
|
||||
}
|
||||
|
||||
function showDialog(linkList) {
|
||||
var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
|
||||
var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value;
|
||||
|
||||
function linkListChangeHandler(e) {
|
||||
var textCtrl = win.find('#text');
|
||||
|
||||
if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) {
|
||||
textCtrl.value(e.control.text());
|
||||
}
|
||||
|
||||
win.find('#href').value(e.control.value());
|
||||
}
|
||||
|
||||
function buildAnchorListControl(url) {
|
||||
var anchorList = [];
|
||||
|
||||
tinymce.each(editor.dom.select('a:not([href])'), function(anchor) {
|
||||
var id = anchor.name || anchor.id;
|
||||
|
||||
if (id) {
|
||||
anchorList.push({
|
||||
text: id,
|
||||
value: '#' + id,
|
||||
selected: url.indexOf('#' + id) != -1
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (anchorList.length) {
|
||||
anchorList.unshift({text: 'None', value: ''});
|
||||
|
||||
return {
|
||||
name: 'anchor',
|
||||
type: 'listbox',
|
||||
label: 'Anchors',
|
||||
values: anchorList,
|
||||
onselect: linkListChangeHandler
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function updateText() {
|
||||
if (!initialText && data.text.length === 0 && onlyText) {
|
||||
this.parent().parent().find('#text')[0].value(this.value());
|
||||
}
|
||||
}
|
||||
|
||||
function urlChange(e) {
|
||||
var meta = e.meta || {};
|
||||
|
||||
if (linkListCtrl) {
|
||||
linkListCtrl.value(editor.convertURL(this.value(), 'href'));
|
||||
}
|
||||
|
||||
tinymce.each(e.meta, function(value, key) {
|
||||
win.find('#' + key).value(value);
|
||||
});
|
||||
|
||||
if (!meta.text) {
|
||||
updateText.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
function isOnlyTextSelected(anchorElm) {
|
||||
var html = selection.getContent();
|
||||
|
||||
// Partial html and not a fully selected anchor element
|
||||
if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anchorElm) {
|
||||
var nodes = anchorElm.childNodes, i;
|
||||
|
||||
if (nodes.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = nodes.length - 1; i >= 0; i--) {
|
||||
if (nodes[i].nodeType != 3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
selectedElm = selection.getNode();
|
||||
anchorElm = dom.getParent(selectedElm, 'a[href]');
|
||||
onlyText = isOnlyTextSelected();
|
||||
|
||||
data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'});
|
||||
data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';
|
||||
|
||||
if ((value = dom.getAttrib(anchorElm, 'target'))) {
|
||||
data.target = value;
|
||||
} else if (editor.settings.default_link_target) {
|
||||
data.target = editor.settings.default_link_target;
|
||||
}
|
||||
|
||||
if ((value = dom.getAttrib(anchorElm, 'rel'))) {
|
||||
data.rel = value;
|
||||
}
|
||||
|
||||
if ((value = dom.getAttrib(anchorElm, 'class'))) {
|
||||
data['class'] = value;
|
||||
}
|
||||
|
||||
if ((value = dom.getAttrib(anchorElm, 'title'))) {
|
||||
data.title = value;
|
||||
}
|
||||
|
||||
if (onlyText) {
|
||||
textListCtrl = {
|
||||
name: 'text',
|
||||
type: 'textbox',
|
||||
size: 40,
|
||||
label: 'Text to display',
|
||||
onchange: function() {
|
||||
data.text = this.value();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (linkList) {
|
||||
linkListCtrl = {
|
||||
type: 'listbox',
|
||||
label: 'Link list',
|
||||
values: buildListItems(
|
||||
linkList,
|
||||
function(item) {
|
||||
item.value = editor.convertURL(item.value || item.url, 'href');
|
||||
},
|
||||
[{text: 'None', value: ''}]
|
||||
),
|
||||
onselect: linkListChangeHandler,
|
||||
value: editor.convertURL(data.href, 'href'),
|
||||
onPostRender: function() {
|
||||
linkListCtrl = this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (editor.settings.target_list !== false) {
|
||||
if (!editor.settings.target_list) {
|
||||
editor.settings.target_list = [
|
||||
{text: 'None', value: ''},
|
||||
{text: 'New window', value: '_blank'}
|
||||
];
|
||||
}
|
||||
|
||||
targetListCtrl = {
|
||||
name: 'target',
|
||||
type: 'listbox',
|
||||
label: 'Target',
|
||||
values: buildListItems(editor.settings.target_list)
|
||||
};
|
||||
}
|
||||
|
||||
if (editor.settings.rel_list) {
|
||||
relListCtrl = {
|
||||
name: 'rel',
|
||||
type: 'listbox',
|
||||
label: 'Rel',
|
||||
values: buildListItems(editor.settings.rel_list)
|
||||
};
|
||||
}
|
||||
|
||||
if (editor.settings.link_class_list) {
|
||||
classListCtrl = {
|
||||
name: 'class',
|
||||
type: 'listbox',
|
||||
label: 'Class',
|
||||
values: buildListItems(
|
||||
editor.settings.link_class_list,
|
||||
function(item) {
|
||||
if (item.value) {
|
||||
item.textStyle = function() {
|
||||
return editor.formatter.getCssText({inline: 'a', classes: [item.value]});
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
if (editor.settings.link_title !== false) {
|
||||
linkTitleCtrl = {
|
||||
name: 'title',
|
||||
type: 'textbox',
|
||||
label: 'Title',
|
||||
value: data.title
|
||||
};
|
||||
}
|
||||
|
||||
win = editor.windowManager.open({
|
||||
title: 'Insert link',
|
||||
data: data,
|
||||
body: [
|
||||
{
|
||||
name: 'href',
|
||||
type: 'filepicker',
|
||||
filetype: 'file',
|
||||
size: 40,
|
||||
autofocus: true,
|
||||
label: 'Url',
|
||||
onchange: urlChange,
|
||||
onkeyup: updateText
|
||||
},
|
||||
textListCtrl,
|
||||
linkTitleCtrl,
|
||||
buildAnchorListControl(data.href),
|
||||
linkListCtrl,
|
||||
relListCtrl,
|
||||
targetListCtrl,
|
||||
classListCtrl
|
||||
],
|
||||
onSubmit: function(e) {
|
||||
var href;
|
||||
|
||||
data = tinymce.extend(data, e.data);
|
||||
href = data.href;
|
||||
|
||||
// Delay confirm since onSubmit will move focus
|
||||
function delayedConfirm(message, callback) {
|
||||
var rng = editor.selection.getRng();
|
||||
|
||||
window.setTimeout(function() {
|
||||
editor.windowManager.confirm(message, function(state) {
|
||||
editor.selection.setRng(rng);
|
||||
callback(state);
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function insertLink() {
|
||||
var linkAttrs = {
|
||||
href: href,
|
||||
target: data.target ? data.target : null,
|
||||
rel: data.rel ? data.rel : null,
|
||||
"class": data["class"] ? data["class"] : null,
|
||||
title: data.title ? data.title : null
|
||||
};
|
||||
|
||||
if (anchorElm) {
|
||||
editor.focus();
|
||||
|
||||
if (onlyText && data.text != initialText) {
|
||||
if ("innerText" in anchorElm) {
|
||||
anchorElm.innerText = data.text;
|
||||
} else {
|
||||
anchorElm.textContent = data.text;
|
||||
}
|
||||
}
|
||||
|
||||
dom.setAttribs(anchorElm, linkAttrs);
|
||||
|
||||
selection.select(anchorElm);
|
||||
editor.undoManager.add();
|
||||
} else {
|
||||
if (onlyText) {
|
||||
editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text)));
|
||||
} else {
|
||||
editor.execCommand('mceInsertLink', false, linkAttrs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!href) {
|
||||
editor.execCommand('unlink');
|
||||
return;
|
||||
}
|
||||
|
||||
// Is email and not //user@domain.com
|
||||
if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) {
|
||||
delayedConfirm(
|
||||
'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
|
||||
function(state) {
|
||||
if (state) {
|
||||
href = 'mailto:' + href;
|
||||
}
|
||||
|
||||
insertLink();
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Is www. prefixed
|
||||
if (/^\s*www\./i.test(href)) {
|
||||
delayedConfirm(
|
||||
'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?',
|
||||
function(state) {
|
||||
if (state) {
|
||||
href = 'http://' + href;
|
||||
}
|
||||
|
||||
insertLink();
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
insertLink();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
editor.addButton('link', {
|
||||
icon: 'link',
|
||||
tooltip: 'Insert/edit link',
|
||||
shortcut: 'Ctrl+K',
|
||||
onclick: createLinkList(showDialog),
|
||||
stateSelector: 'a[href]'
|
||||
});
|
||||
|
||||
editor.addButton('unlink', {
|
||||
icon: 'unlink',
|
||||
tooltip: 'Remove link',
|
||||
cmd: 'unlink',
|
||||
stateSelector: 'a[href]'
|
||||
});
|
||||
|
||||
editor.addShortcut('Ctrl+K', '', createLinkList(showDialog));
|
||||
editor.addCommand('mceLink', createLinkList(showDialog));
|
||||
|
||||
this.showDialog = showDialog;
|
||||
|
||||
editor.addMenuItem('link', {
|
||||
icon: 'link',
|
||||
text: 'Insert link',
|
||||
shortcut: 'Ctrl+K',
|
||||
onclick: createLinkList(showDialog),
|
||||
stateSelector: 'a[href]',
|
||||
context: 'insert',
|
||||
prependToContext: true
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("link",function(a){function b(b){return function(){var c=a.settings.link_list;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):"function"==typeof c?c(b):b(c)}}function c(a,b,c){function d(a,c){return c=c||[],tinymce.each(a,function(a){var e={text:a.text||a.title};a.menu?e.menu=d(a.menu):(e.value=a.value,b&&b(e)),c.push(e)}),c}return d(a,c||[])}function d(b){function d(a){var b=l.find("#text");(!b.value()||a.lastControl&&b.value()==a.lastControl.text())&&b.value(a.control.text()),l.find("#href").value(a.control.value())}function e(b){var c=[];return tinymce.each(a.dom.select("a:not([href])"),function(a){var d=a.name||a.id;d&&c.push({text:d,value:"#"+d,selected:-1!=b.indexOf("#"+d)})}),c.length?(c.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:c,onselect:d}):void 0}function f(){!k&&0===u.text.length&&m&&this.parent().parent().find("#text")[0].value(this.value())}function g(b){var c=b.meta||{};o&&o.value(a.convertURL(this.value(),"href")),tinymce.each(b.meta,function(a,b){l.find("#"+b).value(a)}),c.text||f.call(this)}function h(a){var b=v.getContent();if(/</.test(b)&&(!/^<a [^>]+>[^<]+<\/a>$/.test(b)||-1==b.indexOf("href=")))return!1;if(a){var c,d=a.childNodes;if(0===d.length)return!1;for(c=d.length-1;c>=0;c--)if(3!=d[c].nodeType)return!1}return!0}var i,j,k,l,m,n,o,p,q,r,s,t,u={},v=a.selection,w=a.dom;i=v.getNode(),j=w.getParent(i,"a[href]"),m=h(),u.text=k=j?j.innerText||j.textContent:v.getContent({format:"text"}),u.href=j?w.getAttrib(j,"href"):"",(t=w.getAttrib(j,"target"))?u.target=t:a.settings.default_link_target&&(u.target=a.settings.default_link_target),(t=w.getAttrib(j,"rel"))&&(u.rel=t),(t=w.getAttrib(j,"class"))&&(u["class"]=t),(t=w.getAttrib(j,"title"))&&(u.title=t),m&&(n={name:"text",type:"textbox",size:40,label:"Text to display",onchange:function(){u.text=this.value()}}),b&&(o={type:"listbox",label:"Link list",values:c(b,function(b){b.value=a.convertURL(b.value||b.url,"href")},[{text:"None",value:""}]),onselect:d,value:a.convertURL(u.href,"href"),onPostRender:function(){o=this}}),a.settings.target_list!==!1&&(a.settings.target_list||(a.settings.target_list=[{text:"None",value:""},{text:"New window",value:"_blank"}]),q={name:"target",type:"listbox",label:"Target",values:c(a.settings.target_list)}),a.settings.rel_list&&(p={name:"rel",type:"listbox",label:"Rel",values:c(a.settings.rel_list)}),a.settings.link_class_list&&(r={name:"class",type:"listbox",label:"Class",values:c(a.settings.link_class_list,function(b){b.value&&(b.textStyle=function(){return a.formatter.getCssText({inline:"a",classes:[b.value]})})})}),a.settings.link_title!==!1&&(s={name:"title",type:"textbox",label:"Title",value:u.title}),l=a.windowManager.open({title:"Insert link",data:u,body:[{name:"href",type:"filepicker",filetype:"file",size:40,autofocus:!0,label:"Url",onchange:g,onkeyup:f},n,s,e(u.href),o,p,q,r],onSubmit:function(b){function c(b,c){var d=a.selection.getRng();window.setTimeout(function(){a.windowManager.confirm(b,function(b){a.selection.setRng(d),c(b)})},0)}function d(){var b={href:e,target:u.target?u.target:null,rel:u.rel?u.rel:null,"class":u["class"]?u["class"]:null,title:u.title?u.title:null};j?(a.focus(),m&&u.text!=k&&("innerText"in j?j.innerText=u.text:j.textContent=u.text),w.setAttribs(j,b),v.select(j),a.undoManager.add()):m?a.insertContent(w.createHTML("a",b,w.encode(u.text))):a.execCommand("mceInsertLink",!1,b)}var e;return u=tinymce.extend(u,b.data),(e=u.href)?e.indexOf("@")>0&&-1==e.indexOf("//")&&-1==e.indexOf("mailto:")?void c("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(a){a&&(e="mailto:"+e),d()}):/^\s*www\./i.test(e)?void c("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(a){a&&(e="http://"+e),d()}):void d():void a.execCommand("unlink")}})}a.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Ctrl+K",onclick:b(d),stateSelector:"a[href]"}),a.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),a.addShortcut("Ctrl+K","",b(d)),a.addCommand("mceLink",b(d)),this.showDialog=d,a.addMenuItem("link",{icon:"link",text:"Insert link",shortcut:"Ctrl+K",onclick:b(d),stateSelector:"a[href]",context:"insert",prependToContext:!0})});
|
|
@ -0,0 +1,791 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
/*eslint consistent-this:0 */
|
||||
|
||||
tinymce.PluginManager.add('lists', function(editor) {
|
||||
var self = this;
|
||||
|
||||
function isListNode(node) {
|
||||
return node && (/^(OL|UL|DL)$/).test(node.nodeName);
|
||||
}
|
||||
|
||||
function isFirstChild(node) {
|
||||
return node.parentNode.firstChild == node;
|
||||
}
|
||||
|
||||
function isLastChild(node) {
|
||||
return node.parentNode.lastChild == node;
|
||||
}
|
||||
|
||||
function isTextBlock(node) {
|
||||
return node && !!editor.schema.getTextBlockElements()[node.nodeName];
|
||||
}
|
||||
|
||||
editor.on('init', function() {
|
||||
var dom = editor.dom, selection = editor.selection;
|
||||
|
||||
/**
|
||||
* Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with
|
||||
* index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans
|
||||
* added to them since they can be restored after a dom operation.
|
||||
*
|
||||
* So this: <p><b>|</b><b>|</b></p>
|
||||
* becomes: <p><b><span data-mce-type="bookmark">|</span></b><b data-mce-type="bookmark">|</span></b></p>
|
||||
*
|
||||
* @param {DOMRange} rng DOM Range to get bookmark on.
|
||||
* @return {Object} Bookmark object.
|
||||
*/
|
||||
function createBookmark(rng) {
|
||||
var bookmark = {};
|
||||
|
||||
function setupEndPoint(start) {
|
||||
var offsetNode, container, offset;
|
||||
|
||||
container = rng[start ? 'startContainer' : 'endContainer'];
|
||||
offset = rng[start ? 'startOffset' : 'endOffset'];
|
||||
|
||||
if (container.nodeType == 1) {
|
||||
offsetNode = dom.create('span', {'data-mce-type': 'bookmark'});
|
||||
|
||||
if (container.hasChildNodes()) {
|
||||
offset = Math.min(offset, container.childNodes.length - 1);
|
||||
|
||||
if (start) {
|
||||
container.insertBefore(offsetNode, container.childNodes[offset]);
|
||||
} else {
|
||||
dom.insertAfter(offsetNode, container.childNodes[offset]);
|
||||
}
|
||||
} else {
|
||||
container.appendChild(offsetNode);
|
||||
}
|
||||
|
||||
container = offsetNode;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
bookmark[start ? 'startContainer' : 'endContainer'] = container;
|
||||
bookmark[start ? 'startOffset' : 'endOffset'] = offset;
|
||||
}
|
||||
|
||||
setupEndPoint(true);
|
||||
|
||||
if (!rng.collapsed) {
|
||||
setupEndPoint();
|
||||
}
|
||||
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the selection to the current bookmark and removes any selection container wrappers.
|
||||
*
|
||||
* @param {Object} bookmark Bookmark object to move selection to.
|
||||
*/
|
||||
function moveToBookmark(bookmark) {
|
||||
function restoreEndPoint(start) {
|
||||
var container, offset, node;
|
||||
|
||||
function nodeIndex(container) {
|
||||
var node = container.parentNode.firstChild, idx = 0;
|
||||
|
||||
while (node) {
|
||||
if (node == container) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Skip data-mce-type=bookmark nodes
|
||||
if (node.nodeType != 1 || node.getAttribute('data-mce-type') != 'bookmark') {
|
||||
idx++;
|
||||
}
|
||||
|
||||
node = node.nextSibling;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
container = node = bookmark[start ? 'startContainer' : 'endContainer'];
|
||||
offset = bookmark[start ? 'startOffset' : 'endOffset'];
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.nodeType == 1) {
|
||||
offset = nodeIndex(container);
|
||||
container = container.parentNode;
|
||||
dom.remove(node);
|
||||
}
|
||||
|
||||
bookmark[start ? 'startContainer' : 'endContainer'] = container;
|
||||
bookmark[start ? 'startOffset' : 'endOffset'] = offset;
|
||||
}
|
||||
|
||||
restoreEndPoint(true);
|
||||
restoreEndPoint();
|
||||
|
||||
var rng = dom.createRng();
|
||||
|
||||
rng.setStart(bookmark.startContainer, bookmark.startOffset);
|
||||
|
||||
if (bookmark.endContainer) {
|
||||
rng.setEnd(bookmark.endContainer, bookmark.endOffset);
|
||||
}
|
||||
|
||||
selection.setRng(rng);
|
||||
}
|
||||
|
||||
function createNewTextBlock(contentNode, blockName) {
|
||||
var node, textBlock, fragment = dom.createFragment(), hasContentNode;
|
||||
var blockElements = editor.schema.getBlockElements();
|
||||
|
||||
if (editor.settings.forced_root_block) {
|
||||
blockName = blockName || editor.settings.forced_root_block;
|
||||
}
|
||||
|
||||
if (blockName) {
|
||||
textBlock = dom.create(blockName);
|
||||
|
||||
if (textBlock.tagName === editor.settings.forced_root_block) {
|
||||
dom.setAttribs(textBlock, editor.settings.forced_root_block_attrs);
|
||||
}
|
||||
|
||||
fragment.appendChild(textBlock);
|
||||
}
|
||||
|
||||
if (contentNode) {
|
||||
while ((node = contentNode.firstChild)) {
|
||||
var nodeName = node.nodeName;
|
||||
|
||||
if (!hasContentNode && (nodeName != 'SPAN' || node.getAttribute('data-mce-type') != 'bookmark')) {
|
||||
hasContentNode = true;
|
||||
}
|
||||
|
||||
if (blockElements[nodeName]) {
|
||||
fragment.appendChild(node);
|
||||
textBlock = null;
|
||||
} else {
|
||||
if (blockName) {
|
||||
if (!textBlock) {
|
||||
textBlock = dom.create(blockName);
|
||||
fragment.appendChild(textBlock);
|
||||
}
|
||||
|
||||
textBlock.appendChild(node);
|
||||
} else {
|
||||
fragment.appendChild(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!editor.settings.forced_root_block) {
|
||||
fragment.appendChild(dom.create('br'));
|
||||
} else {
|
||||
// BR is needed in empty blocks on non IE browsers
|
||||
if (!hasContentNode && (!tinymce.Env.ie || tinymce.Env.ie > 10)) {
|
||||
textBlock.appendChild(dom.create('br', {'data-mce-bogus': '1'}));
|
||||
}
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
function getSelectedListItems() {
|
||||
return tinymce.grep(selection.getSelectedBlocks(), function(block) {
|
||||
return /^(LI|DT|DD)$/.test(block.nodeName);
|
||||
});
|
||||
}
|
||||
|
||||
function splitList(ul, li, newBlock) {
|
||||
var tmpRng, fragment;
|
||||
|
||||
var bookmarks = dom.select('span[data-mce-type="bookmark"]', ul);
|
||||
|
||||
newBlock = newBlock || createNewTextBlock(li);
|
||||
tmpRng = dom.createRng();
|
||||
tmpRng.setStartAfter(li);
|
||||
tmpRng.setEndAfter(ul);
|
||||
fragment = tmpRng.extractContents();
|
||||
|
||||
if (!dom.isEmpty(fragment)) {
|
||||
dom.insertAfter(fragment, ul);
|
||||
}
|
||||
|
||||
dom.insertAfter(newBlock, ul);
|
||||
|
||||
if (dom.isEmpty(li.parentNode)) {
|
||||
tinymce.each(bookmarks, function(node) {
|
||||
li.parentNode.parentNode.insertBefore(node, li.parentNode);
|
||||
});
|
||||
|
||||
dom.remove(li.parentNode);
|
||||
}
|
||||
|
||||
dom.remove(li);
|
||||
}
|
||||
|
||||
function mergeWithAdjacentLists(listBlock) {
|
||||
var sibling, node;
|
||||
|
||||
sibling = listBlock.nextSibling;
|
||||
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
|
||||
while ((node = sibling.firstChild)) {
|
||||
listBlock.appendChild(node);
|
||||
}
|
||||
|
||||
dom.remove(sibling);
|
||||
}
|
||||
|
||||
sibling = listBlock.previousSibling;
|
||||
if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
|
||||
while ((node = sibling.firstChild)) {
|
||||
listBlock.insertBefore(node, listBlock.firstChild);
|
||||
}
|
||||
|
||||
dom.remove(sibling);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the all lists in the specified element.
|
||||
*/
|
||||
function normalizeList(element) {
|
||||
tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) {
|
||||
var sibling, parentNode = ul.parentNode;
|
||||
|
||||
// Move UL/OL to previous LI if it's the only child of a LI
|
||||
if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) {
|
||||
sibling = parentNode.previousSibling;
|
||||
if (sibling && sibling.nodeName == 'LI') {
|
||||
sibling.appendChild(ul);
|
||||
|
||||
if (dom.isEmpty(parentNode)) {
|
||||
dom.remove(parentNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4
|
||||
if (isListNode(parentNode)) {
|
||||
sibling = parentNode.previousSibling;
|
||||
if (sibling && sibling.nodeName == 'LI') {
|
||||
sibling.appendChild(ul);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function outdent(li) {
|
||||
var ul = li.parentNode, ulParent = ul.parentNode, newBlock;
|
||||
|
||||
function removeEmptyLi(li) {
|
||||
if (dom.isEmpty(li)) {
|
||||
dom.remove(li);
|
||||
}
|
||||
}
|
||||
|
||||
if (li.nodeName == 'DD') {
|
||||
dom.rename(li, 'DT');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isFirstChild(li) && isLastChild(li)) {
|
||||
if (ulParent.nodeName == "LI") {
|
||||
dom.insertAfter(li, ulParent);
|
||||
removeEmptyLi(ulParent);
|
||||
dom.remove(ul);
|
||||
} else if (isListNode(ulParent)) {
|
||||
dom.remove(ul, true);
|
||||
} else {
|
||||
ulParent.insertBefore(createNewTextBlock(li), ul);
|
||||
dom.remove(ul);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (isFirstChild(li)) {
|
||||
if (ulParent.nodeName == "LI") {
|
||||
dom.insertAfter(li, ulParent);
|
||||
li.appendChild(ul);
|
||||
removeEmptyLi(ulParent);
|
||||
} else if (isListNode(ulParent)) {
|
||||
ulParent.insertBefore(li, ul);
|
||||
} else {
|
||||
ulParent.insertBefore(createNewTextBlock(li), ul);
|
||||
dom.remove(li);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (isLastChild(li)) {
|
||||
if (ulParent.nodeName == "LI") {
|
||||
dom.insertAfter(li, ulParent);
|
||||
} else if (isListNode(ulParent)) {
|
||||
dom.insertAfter(li, ul);
|
||||
} else {
|
||||
dom.insertAfter(createNewTextBlock(li), ul);
|
||||
dom.remove(li);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
if (ulParent.nodeName == 'LI') {
|
||||
ul = ulParent;
|
||||
newBlock = createNewTextBlock(li, 'LI');
|
||||
} else if (isListNode(ulParent)) {
|
||||
newBlock = createNewTextBlock(li, 'LI');
|
||||
} else {
|
||||
newBlock = createNewTextBlock(li);
|
||||
}
|
||||
|
||||
splitList(ul, li, newBlock);
|
||||
normalizeList(ul.parentNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function indent(li) {
|
||||
var sibling, newList;
|
||||
|
||||
function mergeLists(from, to) {
|
||||
var node;
|
||||
|
||||
if (isListNode(from)) {
|
||||
while ((node = li.lastChild.firstChild)) {
|
||||
to.appendChild(node);
|
||||
}
|
||||
|
||||
dom.remove(from);
|
||||
}
|
||||
}
|
||||
|
||||
if (li.nodeName == 'DT') {
|
||||
dom.rename(li, 'DD');
|
||||
return true;
|
||||
}
|
||||
|
||||
sibling = li.previousSibling;
|
||||
|
||||
if (sibling && isListNode(sibling)) {
|
||||
sibling.appendChild(li);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sibling && sibling.nodeName == 'LI' && isListNode(sibling.lastChild)) {
|
||||
sibling.lastChild.appendChild(li);
|
||||
mergeLists(li.lastChild, sibling.lastChild);
|
||||
return true;
|
||||
}
|
||||
|
||||
sibling = li.nextSibling;
|
||||
|
||||
if (sibling && isListNode(sibling)) {
|
||||
sibling.insertBefore(li, sibling.firstChild);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sibling = li.previousSibling;
|
||||
if (sibling && sibling.nodeName == 'LI') {
|
||||
newList = dom.create(li.parentNode.nodeName);
|
||||
sibling.appendChild(newList);
|
||||
newList.appendChild(li);
|
||||
mergeLists(li.lastChild, newList);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function indentSelection() {
|
||||
var listElements = getSelectedListItems();
|
||||
|
||||
if (listElements.length) {
|
||||
var bookmark = createBookmark(selection.getRng(true));
|
||||
|
||||
for (var i = 0; i < listElements.length; i++) {
|
||||
if (!indent(listElements[i]) && i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
moveToBookmark(bookmark);
|
||||
editor.nodeChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function outdentSelection() {
|
||||
var listElements = getSelectedListItems();
|
||||
|
||||
if (listElements.length) {
|
||||
var bookmark = createBookmark(selection.getRng(true));
|
||||
var i, y, root = editor.getBody();
|
||||
|
||||
i = listElements.length;
|
||||
while (i--) {
|
||||
var node = listElements[i].parentNode;
|
||||
|
||||
while (node && node != root) {
|
||||
y = listElements.length;
|
||||
while (y--) {
|
||||
if (listElements[y] === node) {
|
||||
listElements.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < listElements.length; i++) {
|
||||
if (!outdent(listElements[i]) && i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
moveToBookmark(bookmark);
|
||||
editor.nodeChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function applyList(listName) {
|
||||
var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI';
|
||||
|
||||
listName = listName.toUpperCase();
|
||||
|
||||
if (listName == 'DL') {
|
||||
listItemName = 'DT';
|
||||
}
|
||||
|
||||
function getSelectedTextBlocks() {
|
||||
var textBlocks = [], root = editor.getBody();
|
||||
|
||||
function getEndPointNode(start) {
|
||||
var container, offset;
|
||||
|
||||
container = rng[start ? 'startContainer' : 'endContainer'];
|
||||
offset = rng[start ? 'startOffset' : 'endOffset'];
|
||||
|
||||
// Resolve node index
|
||||
if (container.nodeType == 1) {
|
||||
container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
|
||||
}
|
||||
|
||||
while (container.parentNode != root) {
|
||||
if (isTextBlock(container)) {
|
||||
return container;
|
||||
}
|
||||
|
||||
if (/^(TD|TH)$/.test(container.parentNode.nodeName)) {
|
||||
return container;
|
||||
}
|
||||
|
||||
container = container.parentNode;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
var startNode = getEndPointNode(true);
|
||||
var endNode = getEndPointNode();
|
||||
var block, siblings = [];
|
||||
|
||||
for (var node = startNode; node; node = node.nextSibling) {
|
||||
siblings.push(node);
|
||||
|
||||
if (node == endNode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tinymce.each(siblings, function(node) {
|
||||
if (isTextBlock(node)) {
|
||||
textBlocks.push(node);
|
||||
block = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dom.isBlock(node) || node.nodeName == 'BR') {
|
||||
if (node.nodeName == 'BR') {
|
||||
dom.remove(node);
|
||||
}
|
||||
|
||||
block = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var nextSibling = node.nextSibling;
|
||||
if (tinymce.dom.BookmarkManager.isBookmarkNode(node)) {
|
||||
if (isTextBlock(nextSibling) || (!nextSibling && node.parentNode == root)) {
|
||||
block = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block) {
|
||||
block = dom.create('p');
|
||||
node.parentNode.insertBefore(block, node);
|
||||
textBlocks.push(block);
|
||||
}
|
||||
|
||||
block.appendChild(node);
|
||||
});
|
||||
|
||||
return textBlocks;
|
||||
}
|
||||
|
||||
tinymce.each(getSelectedTextBlocks(), function(block) {
|
||||
var listBlock, sibling;
|
||||
|
||||
sibling = block.previousSibling;
|
||||
if (sibling && isListNode(sibling) && sibling.nodeName == listName) {
|
||||
listBlock = sibling;
|
||||
block = dom.rename(block, listItemName);
|
||||
sibling.appendChild(block);
|
||||
} else {
|
||||
listBlock = dom.create(listName);
|
||||
block.parentNode.insertBefore(listBlock, block);
|
||||
listBlock.appendChild(block);
|
||||
block = dom.rename(block, listItemName);
|
||||
}
|
||||
|
||||
mergeWithAdjacentLists(listBlock);
|
||||
});
|
||||
|
||||
moveToBookmark(bookmark);
|
||||
}
|
||||
|
||||
function removeList() {
|
||||
var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody();
|
||||
|
||||
tinymce.each(getSelectedListItems(), function(li) {
|
||||
var node, rootList;
|
||||
|
||||
if (dom.isEmpty(li)) {
|
||||
outdent(li);
|
||||
return;
|
||||
}
|
||||
|
||||
for (node = li; node && node != root; node = node.parentNode) {
|
||||
if (isListNode(node)) {
|
||||
rootList = node;
|
||||
}
|
||||
}
|
||||
|
||||
splitList(rootList, li);
|
||||
});
|
||||
|
||||
moveToBookmark(bookmark);
|
||||
}
|
||||
|
||||
function toggleList(listName) {
|
||||
var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
|
||||
|
||||
if (parentList) {
|
||||
if (parentList.nodeName == listName) {
|
||||
removeList(listName);
|
||||
} else {
|
||||
var bookmark = createBookmark(selection.getRng(true));
|
||||
mergeWithAdjacentLists(dom.rename(parentList, listName));
|
||||
moveToBookmark(bookmark);
|
||||
}
|
||||
} else {
|
||||
applyList(listName);
|
||||
}
|
||||
}
|
||||
|
||||
function queryListCommandState(listName) {
|
||||
return function() {
|
||||
var parentList = dom.getParent(editor.selection.getStart(), 'UL,OL,DL');
|
||||
|
||||
return parentList && parentList.nodeName == listName;
|
||||
};
|
||||
}
|
||||
|
||||
self.backspaceDelete = function(isForward) {
|
||||
function findNextCaretContainer(rng, isForward) {
|
||||
var node = rng.startContainer, offset = rng.startOffset;
|
||||
var nonEmptyBlocks, walker;
|
||||
|
||||
if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
nonEmptyBlocks = editor.schema.getNonEmptyElements();
|
||||
walker = new tinymce.dom.TreeWalker(rng.startContainer);
|
||||
|
||||
while ((node = walker[isForward ? 'next' : 'prev']())) {
|
||||
if (node.nodeName == 'LI' && !node.hasChildNodes()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (nonEmptyBlocks[node.nodeName]) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.nodeType == 3 && node.data.length > 0) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function mergeLiElements(fromElm, toElm) {
|
||||
var node, listNode, ul = fromElm.parentNode;
|
||||
|
||||
if (isListNode(toElm.lastChild)) {
|
||||
listNode = toElm.lastChild;
|
||||
}
|
||||
|
||||
node = toElm.lastChild;
|
||||
if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) {
|
||||
dom.remove(node);
|
||||
}
|
||||
|
||||
if (dom.isEmpty(toElm)) {
|
||||
dom.$(toElm).empty();
|
||||
}
|
||||
|
||||
if (!dom.isEmpty(fromElm)) {
|
||||
while ((node = fromElm.firstChild)) {
|
||||
toElm.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (listNode) {
|
||||
toElm.appendChild(listNode);
|
||||
}
|
||||
|
||||
dom.remove(fromElm);
|
||||
|
||||
if (dom.isEmpty(ul)) {
|
||||
dom.remove(ul);
|
||||
}
|
||||
}
|
||||
|
||||
if (selection.isCollapsed()) {
|
||||
var li = dom.getParent(selection.getStart(), 'LI');
|
||||
|
||||
if (li) {
|
||||
var rng = selection.getRng(true);
|
||||
var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
|
||||
|
||||
if (otherLi && otherLi != li) {
|
||||
var bookmark = createBookmark(rng);
|
||||
|
||||
if (isForward) {
|
||||
mergeLiElements(otherLi, li);
|
||||
} else {
|
||||
mergeLiElements(li, otherLi);
|
||||
}
|
||||
|
||||
moveToBookmark(bookmark);
|
||||
|
||||
return true;
|
||||
} else if (!otherLi) {
|
||||
if (!isForward && removeList(li.parentNode.nodeName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
editor.addCommand('Indent', function() {
|
||||
if (!indentSelection()) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
editor.addCommand('Outdent', function() {
|
||||
if (!outdentSelection()) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
editor.addCommand('InsertUnorderedList', function() {
|
||||
toggleList('UL');
|
||||
});
|
||||
|
||||
editor.addCommand('InsertOrderedList', function() {
|
||||
toggleList('OL');
|
||||
});
|
||||
|
||||
editor.addCommand('InsertDefinitionList', function() {
|
||||
toggleList('DL');
|
||||
});
|
||||
|
||||
editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL'));
|
||||
editor.addQueryStateHandler('InsertOrderedList', queryListCommandState('OL'));
|
||||
editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState('DL'));
|
||||
|
||||
editor.on('keydown', function(e) {
|
||||
// Check for tab but not ctrl/cmd+tab since it switches browser tabs
|
||||
if (e.keyCode != 9 || tinymce.util.VK.metaKeyPressed(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD')) {
|
||||
e.preventDefault();
|
||||
|
||||
if (e.shiftKey) {
|
||||
outdentSelection();
|
||||
} else {
|
||||
indentSelection();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.addButton('indent', {
|
||||
icon: 'indent',
|
||||
title: 'Increase indent',
|
||||
cmd: 'Indent',
|
||||
onPostRender: function() {
|
||||
var ctrl = this;
|
||||
|
||||
editor.on('nodechange', function() {
|
||||
var blocks = editor.selection.getSelectedBlocks();
|
||||
var disable = false;
|
||||
|
||||
for (var i = 0, l = blocks.length; !disable && i < l; i++) {
|
||||
var tag = blocks[i].nodeName;
|
||||
|
||||
disable = (tag == 'LI' && isFirstChild(blocks[i]) || tag == 'UL' || tag == 'OL' || tag == 'DD');
|
||||
}
|
||||
|
||||
ctrl.disabled(disable);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('keydown', function(e) {
|
||||
if (e.keyCode == tinymce.util.VK.BACKSPACE) {
|
||||
if (self.backspaceDelete()) {
|
||||
e.preventDefault();
|
||||
}
|
||||
} else if (e.keyCode == tinymce.util.VK.DELETE) {
|
||||
if (self.backspaceDelete(true)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,774 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*jshint maxlen:255 */
|
||||
/*eslint max-len:0 */
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('media', function(editor, url) {
|
||||
var urlPatterns = [
|
||||
{regex: /youtu\.be\/([\w\-.]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$1'},
|
||||
{regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$2'},
|
||||
{regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: '//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc'},
|
||||
{regex: /vimeo\.com\/(.*)\/([0-9]+)/, type: "iframe", w: 425, h: 350, url: "//player.vimeo.com/video/$2?title=0&byline=0"},
|
||||
{regex: /maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/, type: 'iframe', w: 425, h: 350, url: '//maps.google.com/maps/ms?msid=$2&output=embed"'}
|
||||
];
|
||||
|
||||
var embedChange = (tinymce.Env.ie && tinymce.Env.ie <= 8) ? 'onChange' : 'onInput';
|
||||
|
||||
function guessMime(url) {
|
||||
if (url.indexOf('.mp3') != -1) {
|
||||
return 'audio/mpeg';
|
||||
}
|
||||
|
||||
if (url.indexOf('.wav') != -1) {
|
||||
return 'audio/wav';
|
||||
}
|
||||
|
||||
if (url.indexOf('.mp4') != -1) {
|
||||
return 'video/mp4';
|
||||
}
|
||||
|
||||
if (url.indexOf('.webm') != -1) {
|
||||
return 'video/webm';
|
||||
}
|
||||
|
||||
if (url.indexOf('.ogg') != -1) {
|
||||
return 'video/ogg';
|
||||
}
|
||||
|
||||
if (url.indexOf('.swf') != -1) {
|
||||
return 'application/x-shockwave-flash';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function getVideoScriptMatch(src) {
|
||||
var prefixes = editor.settings.media_scripts;
|
||||
|
||||
if (prefixes) {
|
||||
for (var i = 0; i < prefixes.length; i++) {
|
||||
if (src.indexOf(prefixes[i].filter) !== -1) {
|
||||
return prefixes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
var win, width, height, data;
|
||||
|
||||
var generalFormItems = [
|
||||
{
|
||||
name: 'source1',
|
||||
type: 'filepicker',
|
||||
filetype: 'media',
|
||||
size: 40,
|
||||
autofocus: true,
|
||||
label: 'Source',
|
||||
onchange: function(e) {
|
||||
tinymce.each(e.meta, function(value, key) {
|
||||
win.find('#' + key).value(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function recalcSize(e) {
|
||||
var widthCtrl, heightCtrl, newWidth, newHeight;
|
||||
|
||||
widthCtrl = win.find('#width')[0];
|
||||
heightCtrl = win.find('#height')[0];
|
||||
|
||||
newWidth = widthCtrl.value();
|
||||
newHeight = heightCtrl.value();
|
||||
|
||||
if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
|
||||
if (e.control == widthCtrl) {
|
||||
newHeight = Math.round((newWidth / width) * newHeight);
|
||||
heightCtrl.value(newHeight);
|
||||
} else {
|
||||
newWidth = Math.round((newHeight / height) * newWidth);
|
||||
widthCtrl.value(newWidth);
|
||||
}
|
||||
}
|
||||
|
||||
width = newWidth;
|
||||
height = newHeight;
|
||||
}
|
||||
|
||||
if (editor.settings.media_alt_source !== false) {
|
||||
generalFormItems.push({name: 'source2', type: 'filepicker', filetype: 'media', size: 40, label: 'Alternative source'});
|
||||
}
|
||||
|
||||
if (editor.settings.media_poster !== false) {
|
||||
generalFormItems.push({name: 'poster', type: 'filepicker', filetype: 'image', size: 40, label: 'Poster'});
|
||||
}
|
||||
|
||||
if (editor.settings.media_dimensions !== false) {
|
||||
generalFormItems.push({
|
||||
type: 'container',
|
||||
label: 'Dimensions',
|
||||
layout: 'flex',
|
||||
align: 'center',
|
||||
spacing: 5,
|
||||
items: [
|
||||
{name: 'width', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
|
||||
{type: 'label', text: 'x'},
|
||||
{name: 'height', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
|
||||
{name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
data = getData(editor.selection.getNode());
|
||||
width = data.width;
|
||||
height = data.height;
|
||||
|
||||
var embedTextBox = {
|
||||
id: 'mcemediasource',
|
||||
type: 'textbox',
|
||||
flex: 1,
|
||||
name: 'embed',
|
||||
value: getSource(),
|
||||
multiline: true,
|
||||
label: 'Source'
|
||||
};
|
||||
|
||||
function updateValueOnChange() {
|
||||
data = htmlToData(this.value());
|
||||
this.parent().parent().fromJSON(data);
|
||||
}
|
||||
|
||||
embedTextBox[embedChange] = updateValueOnChange;
|
||||
|
||||
win = editor.windowManager.open({
|
||||
title: 'Insert/edit video',
|
||||
data: data,
|
||||
bodyType: 'tabpanel',
|
||||
body: [
|
||||
{
|
||||
title: 'General',
|
||||
type: "form",
|
||||
onShowTab: function() {
|
||||
data = htmlToData(this.next().find('#embed').value());
|
||||
this.fromJSON(data);
|
||||
},
|
||||
items: generalFormItems
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Embed',
|
||||
type: "panel",
|
||||
layout: 'flex',
|
||||
direction: 'column',
|
||||
align: 'stretch',
|
||||
padding: 10,
|
||||
spacing: 10,
|
||||
onShowTab: function() {
|
||||
this.find('#embed').value(dataToHtml(this.parent().toJSON()));
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'label',
|
||||
text: 'Paste your embed code below:',
|
||||
forId: 'mcemediasource'
|
||||
},
|
||||
embedTextBox
|
||||
]
|
||||
}
|
||||
],
|
||||
onSubmit: function() {
|
||||
var beforeObjects, afterObjects, i, y;
|
||||
|
||||
beforeObjects = editor.dom.select('img[data-mce-object]');
|
||||
editor.insertContent(dataToHtml(this.toJSON()));
|
||||
afterObjects = editor.dom.select('img[data-mce-object]');
|
||||
|
||||
// Find new image placeholder so we can select it
|
||||
for (i = 0; i < beforeObjects.length; i++) {
|
||||
for (y = afterObjects.length - 1; y >= 0; y--) {
|
||||
if (beforeObjects[i] == afterObjects[y]) {
|
||||
afterObjects.splice(y, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.selection.select(afterObjects[0]);
|
||||
editor.nodeChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSource() {
|
||||
var elm = editor.selection.getNode();
|
||||
|
||||
if (elm.getAttribute('data-mce-object')) {
|
||||
return editor.selection.getContent();
|
||||
}
|
||||
}
|
||||
|
||||
function dataToHtml(data) {
|
||||
var html = '';
|
||||
|
||||
if (!data.source1) {
|
||||
tinymce.extend(data, htmlToData(data.embed));
|
||||
if (!data.source1) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!data.source2) {
|
||||
data.source2 = '';
|
||||
}
|
||||
|
||||
if (!data.poster) {
|
||||
data.poster = '';
|
||||
}
|
||||
|
||||
data.source1 = editor.convertURL(data.source1, "source");
|
||||
data.source2 = editor.convertURL(data.source2, "source");
|
||||
data.source1mime = guessMime(data.source1);
|
||||
data.source2mime = guessMime(data.source2);
|
||||
data.poster = editor.convertURL(data.poster, "poster");
|
||||
data.flashPlayerUrl = editor.convertURL(url + '/moxieplayer.swf', "movie");
|
||||
|
||||
tinymce.each(urlPatterns, function(pattern) {
|
||||
var match, i, url;
|
||||
|
||||
if ((match = pattern.regex.exec(data.source1))) {
|
||||
url = pattern.url;
|
||||
|
||||
for (i = 0; match[i]; i++) {
|
||||
/*jshint loopfunc:true*/
|
||||
/*eslint no-loop-func:0 */
|
||||
url = url.replace('$' + i, function() {
|
||||
return match[i];
|
||||
});
|
||||
}
|
||||
|
||||
data.source1 = url;
|
||||
data.type = pattern.type;
|
||||
data.width = data.width || pattern.w;
|
||||
data.height = data.height || pattern.h;
|
||||
}
|
||||
});
|
||||
|
||||
if (data.embed) {
|
||||
html = updateHtml(data.embed, data, true);
|
||||
} else {
|
||||
var videoScript = getVideoScriptMatch(data.source1);
|
||||
if (videoScript) {
|
||||
data.type = 'script';
|
||||
data.width = videoScript.width;
|
||||
data.height = videoScript.height;
|
||||
}
|
||||
|
||||
data.width = data.width || 300;
|
||||
data.height = data.height || 150;
|
||||
|
||||
tinymce.each(data, function(value, key) {
|
||||
data[key] = editor.dom.encode(value);
|
||||
});
|
||||
|
||||
if (data.type == "iframe") {
|
||||
html += '<iframe src="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '"></iframe>';
|
||||
} else if (data.source1mime == "application/x-shockwave-flash") {
|
||||
html += '<object data="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '" type="application/x-shockwave-flash">';
|
||||
|
||||
if (data.poster) {
|
||||
html += '<img src="' + data.poster + '" width="' + data.width + '" height="' + data.height + '" />';
|
||||
}
|
||||
|
||||
html += '</object>';
|
||||
} else if (data.source1mime.indexOf('audio') != -1) {
|
||||
if (editor.settings.audio_template_callback) {
|
||||
html = editor.settings.audio_template_callback(data);
|
||||
} else {
|
||||
html += (
|
||||
'<audio controls="controls" src="' + data.source1 + '">' +
|
||||
(data.source2 ? '\n<source src="' + data.source2 + '"' + (data.source2mime ? ' type="' + data.source2mime + '"' : '') + ' />\n' : '') +
|
||||
'</audio>'
|
||||
);
|
||||
}
|
||||
} else if (data.type == "script") {
|
||||
html += '<script src="' + data.source1 + '"></script>';
|
||||
} else {
|
||||
if (editor.settings.video_template_callback) {
|
||||
html = editor.settings.video_template_callback(data);
|
||||
} else {
|
||||
html = (
|
||||
'<video width="' + data.width + '" height="' + data.height + '"' + (data.poster ? ' poster="' + data.poster + '"' : '') + ' controls="controls">\n' +
|
||||
'<source src="' + data.source1 + '"' + (data.source1mime ? ' type="' + data.source1mime + '"' : '') + ' />\n' +
|
||||
(data.source2 ? '<source src="' + data.source2 + '"' + (data.source2mime ? ' type="' + data.source2mime + '"' : '') + ' />\n' : '') +
|
||||
'</video>'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function htmlToData(html) {
|
||||
var data = {};
|
||||
|
||||
new tinymce.html.SaxParser({
|
||||
validate: false,
|
||||
allow_conditional_comments: true,
|
||||
special: 'script,noscript',
|
||||
start: function(name, attrs) {
|
||||
if (!data.source1 && name == "param") {
|
||||
data.source1 = attrs.map.movie;
|
||||
}
|
||||
|
||||
if (name == "iframe" || name == "object" || name == "embed" || name == "video" || name == "audio") {
|
||||
if (!data.type) {
|
||||
data.type = name;
|
||||
}
|
||||
|
||||
data = tinymce.extend(attrs.map, data);
|
||||
}
|
||||
|
||||
if (name == "script") {
|
||||
var videoScript = getVideoScriptMatch(attrs.map.src);
|
||||
if (!videoScript) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = {
|
||||
type: "script",
|
||||
source1: attrs.map.src,
|
||||
width: videoScript.width,
|
||||
height: videoScript.height
|
||||
};
|
||||
}
|
||||
|
||||
if (name == "source") {
|
||||
if (!data.source1) {
|
||||
data.source1 = attrs.map.src;
|
||||
} else if (!data.source2) {
|
||||
data.source2 = attrs.map.src;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "img" && !data.poster) {
|
||||
data.poster = attrs.map.src;
|
||||
}
|
||||
}
|
||||
}).parse(html);
|
||||
|
||||
data.source1 = data.source1 || data.src || data.data;
|
||||
data.source2 = data.source2 || '';
|
||||
data.poster = data.poster || '';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function getData(element) {
|
||||
if (element.getAttribute('data-mce-object')) {
|
||||
return htmlToData(editor.serializer.serialize(element, {selection: true}));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function sanitize(html) {
|
||||
if (editor.settings.media_filter_html === false) {
|
||||
return html;
|
||||
}
|
||||
|
||||
var writer = new tinymce.html.Writer();
|
||||
|
||||
new tinymce.html.SaxParser({
|
||||
validate: false,
|
||||
allow_conditional_comments: false,
|
||||
special: 'script,noscript',
|
||||
|
||||
comment: function(text) {
|
||||
writer.comment(text);
|
||||
},
|
||||
|
||||
cdata: function(text) {
|
||||
writer.cdata(text);
|
||||
},
|
||||
|
||||
text: function(text, raw) {
|
||||
writer.text(text, raw);
|
||||
},
|
||||
|
||||
start: function(name, attrs, empty) {
|
||||
if (name == 'script' || name == 'noscript') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < attrs.length; i++) {
|
||||
if (attrs[i].name.indexOf('on') === 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
writer.start(name, attrs, empty);
|
||||
},
|
||||
|
||||
end: function(name) {
|
||||
if (name == 'script' || name == 'noscript') {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.end(name);
|
||||
}
|
||||
}, new tinymce.html.Schema({})).parse(html);
|
||||
|
||||
return writer.getContent();
|
||||
}
|
||||
|
||||
function updateHtml(html, data, updateAll) {
|
||||
var writer = new tinymce.html.Writer();
|
||||
var sourceCount = 0, hasImage;
|
||||
|
||||
function setAttributes(attrs, updatedAttrs) {
|
||||
var name, i, value, attr;
|
||||
|
||||
for (name in updatedAttrs) {
|
||||
value = "" + updatedAttrs[name];
|
||||
|
||||
if (attrs.map[name]) {
|
||||
i = attrs.length;
|
||||
while (i--) {
|
||||
attr = attrs[i];
|
||||
|
||||
if (attr.name == name) {
|
||||
if (value) {
|
||||
attrs.map[name] = value;
|
||||
attr.value = value;
|
||||
} else {
|
||||
delete attrs.map[name];
|
||||
attrs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (value) {
|
||||
attrs.push({
|
||||
name: name,
|
||||
value: value
|
||||
});
|
||||
|
||||
attrs.map[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new tinymce.html.SaxParser({
|
||||
validate: false,
|
||||
allow_conditional_comments: true,
|
||||
special: 'script,noscript',
|
||||
|
||||
comment: function(text) {
|
||||
writer.comment(text);
|
||||
},
|
||||
|
||||
cdata: function(text) {
|
||||
writer.cdata(text);
|
||||
},
|
||||
|
||||
text: function(text, raw) {
|
||||
writer.text(text, raw);
|
||||
},
|
||||
|
||||
start: function(name, attrs, empty) {
|
||||
switch (name) {
|
||||
case "video":
|
||||
case "object":
|
||||
case "embed":
|
||||
case "img":
|
||||
case "iframe":
|
||||
setAttributes(attrs, {
|
||||
width: data.width,
|
||||
height: data.height
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
if (updateAll) {
|
||||
switch (name) {
|
||||
case "video":
|
||||
setAttributes(attrs, {
|
||||
poster: data.poster,
|
||||
src: ""
|
||||
});
|
||||
|
||||
if (data.source2) {
|
||||
setAttributes(attrs, {
|
||||
src: ""
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "iframe":
|
||||
setAttributes(attrs, {
|
||||
src: data.source1
|
||||
});
|
||||
break;
|
||||
|
||||
case "source":
|
||||
sourceCount++;
|
||||
|
||||
if (sourceCount <= 2) {
|
||||
setAttributes(attrs, {
|
||||
src: data["source" + sourceCount],
|
||||
type: data["source" + sourceCount + "mime"]
|
||||
});
|
||||
|
||||
if (!data["source" + sourceCount]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "img":
|
||||
if (!data.poster) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasImage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
writer.start(name, attrs, empty);
|
||||
},
|
||||
|
||||
end: function(name) {
|
||||
if (name == "video" && updateAll) {
|
||||
for (var index = 1; index <= 2; index++) {
|
||||
if (data["source" + index]) {
|
||||
var attrs = [];
|
||||
attrs.map = {};
|
||||
|
||||
if (sourceCount < index) {
|
||||
setAttributes(attrs, {
|
||||
src: data["source" + index],
|
||||
type: data["source" + index + "mime"]
|
||||
});
|
||||
|
||||
writer.start("source", attrs, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.poster && name == "object" && updateAll && !hasImage) {
|
||||
var imgAttrs = [];
|
||||
imgAttrs.map = {};
|
||||
|
||||
setAttributes(imgAttrs, {
|
||||
src: data.poster,
|
||||
width: data.width,
|
||||
height: data.height
|
||||
});
|
||||
|
||||
writer.start("img", imgAttrs, true);
|
||||
}
|
||||
|
||||
writer.end(name);
|
||||
}
|
||||
}, new tinymce.html.Schema({})).parse(html);
|
||||
|
||||
return writer.getContent();
|
||||
}
|
||||
|
||||
editor.on('ResolveName', function(e) {
|
||||
var name;
|
||||
|
||||
if (e.target.nodeType == 1 && (name = e.target.getAttribute("data-mce-object"))) {
|
||||
e.name = name;
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('preInit', function() {
|
||||
// Make sure that any messy HTML is retained inside these
|
||||
var specialElements = editor.schema.getSpecialElements();
|
||||
tinymce.each('video audio iframe object'.split(' '), function(name) {
|
||||
specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
|
||||
});
|
||||
|
||||
// Allow elements
|
||||
//editor.schema.addValidElements('object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]');
|
||||
|
||||
// Set allowFullscreen attribs as boolean
|
||||
var boolAttrs = editor.schema.getBoolAttrs();
|
||||
tinymce.each('webkitallowfullscreen mozallowfullscreen allowfullscreen'.split(' '), function(name) {
|
||||
boolAttrs[name] = {};
|
||||
});
|
||||
|
||||
// Converts iframe, video etc into placeholder images
|
||||
editor.parser.addNodeFilter('iframe,video,audio,object,embed,script', function(nodes, name) {
|
||||
var i = nodes.length, ai, node, placeHolder, attrName, attrValue, attribs, innerHtml;
|
||||
var videoScript;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
if (!node.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.name == 'script') {
|
||||
videoScript = getVideoScriptMatch(node.attr('src'));
|
||||
if (!videoScript) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
placeHolder = new tinymce.html.Node('img', 1);
|
||||
placeHolder.shortEnded = true;
|
||||
|
||||
if (videoScript) {
|
||||
if (videoScript.width) {
|
||||
node.attr('width', videoScript.width.toString());
|
||||
}
|
||||
|
||||
if (videoScript.height) {
|
||||
node.attr('height', videoScript.height.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix all attributes except width, height and style since we
|
||||
// will add these to the placeholder
|
||||
attribs = node.attributes;
|
||||
ai = attribs.length;
|
||||
while (ai--) {
|
||||
attrName = attribs[ai].name;
|
||||
attrValue = attribs[ai].value;
|
||||
|
||||
if (attrName !== "width" && attrName !== "height" && attrName !== "style") {
|
||||
if (attrName == "data" || attrName == "src") {
|
||||
attrValue = editor.convertURL(attrValue, attrName);
|
||||
}
|
||||
|
||||
placeHolder.attr('data-mce-p-' + attrName, attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Place the inner HTML contents inside an escaped attribute
|
||||
// This enables us to copy/paste the fake object
|
||||
innerHtml = node.firstChild && node.firstChild.value;
|
||||
if (innerHtml) {
|
||||
placeHolder.attr("data-mce-html", escape(innerHtml));
|
||||
placeHolder.firstChild = null;
|
||||
}
|
||||
|
||||
placeHolder.attr({
|
||||
width: node.attr('width') || "300",
|
||||
height: node.attr('height') || (name == "audio" ? "30" : "150"),
|
||||
style: node.attr('style'),
|
||||
src: tinymce.Env.transparentSrc,
|
||||
"data-mce-object": name,
|
||||
"class": "mce-object mce-object-" + name
|
||||
});
|
||||
|
||||
node.replace(placeHolder);
|
||||
}
|
||||
});
|
||||
|
||||
// Replaces placeholder images with real elements for video, object, iframe etc
|
||||
editor.serializer.addAttributeFilter('data-mce-object', function(nodes, name) {
|
||||
var i = nodes.length, node, realElm, ai, attribs, innerHtml, innerNode, realElmName;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
if (!node.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
realElmName = node.attr(name);
|
||||
realElm = new tinymce.html.Node(realElmName, 1);
|
||||
|
||||
// Add width/height to everything but audio
|
||||
if (realElmName != "audio" && realElmName != "script") {
|
||||
realElm.attr({
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height')
|
||||
});
|
||||
}
|
||||
|
||||
realElm.attr({
|
||||
style: node.attr('style')
|
||||
});
|
||||
|
||||
// Unprefix all placeholder attributes
|
||||
attribs = node.attributes;
|
||||
ai = attribs.length;
|
||||
while (ai--) {
|
||||
var attrName = attribs[ai].name;
|
||||
|
||||
if (attrName.indexOf('data-mce-p-') === 0) {
|
||||
realElm.attr(attrName.substr(11), attribs[ai].value);
|
||||
}
|
||||
}
|
||||
|
||||
if (realElmName == "script") {
|
||||
realElm.attr('type', 'text/javascript');
|
||||
}
|
||||
|
||||
// Inject innerhtml
|
||||
innerHtml = node.attr('data-mce-html');
|
||||
if (innerHtml) {
|
||||
innerNode = new tinymce.html.Node('#text', 3);
|
||||
innerNode.raw = true;
|
||||
innerNode.value = sanitize(unescape(innerHtml));
|
||||
realElm.append(innerNode);
|
||||
}
|
||||
|
||||
node.replace(realElm);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.on('ObjectSelected', function(e) {
|
||||
var objectType = e.target.getAttribute('data-mce-object');
|
||||
|
||||
if (objectType == "audio" || objectType == "script") {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('objectResized', function(e) {
|
||||
var target = e.target, html;
|
||||
|
||||
if (target.getAttribute('data-mce-object')) {
|
||||
html = target.getAttribute('data-mce-html');
|
||||
if (html) {
|
||||
html = unescape(html);
|
||||
target.setAttribute('data-mce-html', escape(
|
||||
updateHtml(html, {
|
||||
width: e.width,
|
||||
height: e.height
|
||||
})
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.addButton('media', {
|
||||
tooltip: 'Insert/edit video',
|
||||
onclick: showDialog,
|
||||
stateSelector: ['img[data-mce-object=video]', 'img[data-mce-object=iframe]']
|
||||
});
|
||||
|
||||
editor.addMenuItem('media', {
|
||||
icon: 'media',
|
||||
text: 'Insert video',
|
||||
onclick: showDialog,
|
||||
context: 'insert',
|
||||
prependToContext: true
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('nonbreaking', function(editor) {
|
||||
var setting = editor.getParam('nonbreaking_force_tab');
|
||||
|
||||
editor.addCommand('mceNonBreaking', function() {
|
||||
editor.insertContent(
|
||||
(editor.plugins.visualchars && editor.plugins.visualchars.state) ?
|
||||
'<span class="mce-nbsp"> </span>' : ' '
|
||||
);
|
||||
|
||||
editor.dom.setAttrib(editor.dom.select('span.mce-nbsp'), 'data-mce-bogus', '1');
|
||||
});
|
||||
|
||||
editor.addButton('nonbreaking', {
|
||||
title: 'Nonbreaking space',
|
||||
cmd: 'mceNonBreaking'
|
||||
});
|
||||
|
||||
editor.addMenuItem('nonbreaking', {
|
||||
text: 'Nonbreaking space',
|
||||
cmd: 'mceNonBreaking',
|
||||
context: 'insert'
|
||||
});
|
||||
|
||||
if (setting) {
|
||||
var spaces = +setting > 1 ? +setting : 3; // defaults to 3 spaces if setting is true (or 1)
|
||||
|
||||
editor.on('keydown', function(e) {
|
||||
if (e.keyCode == 9) {
|
||||
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
for (var i = 0; i < spaces; i++) {
|
||||
editor.execCommand('mceNonBreaking');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("nonbreaking",function(a){var b=a.getParam("nonbreaking_force_tab");if(a.addCommand("mceNonBreaking",function(){a.insertContent(a.plugins.visualchars&&a.plugins.visualchars.state?'<span class="mce-nbsp"> </span>':" "),a.dom.setAttrib(a.dom.select("span.mce-nbsp"),"data-mce-bogus","1")}),a.addButton("nonbreaking",{title:"Nonbreaking space",cmd:"mceNonBreaking"}),a.addMenuItem("nonbreaking",{text:"Nonbreaking space",cmd:"mceNonBreaking",context:"insert"}),b){var c=+b>1?+b:3;a.on("keydown",function(b){if(9==b.keyCode){if(b.shiftKey)return;b.preventDefault();for(var d=0;c>d;d++)a.execCommand("mceNonBreaking")}})}});
|
|
@ -0,0 +1,540 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*jshint loopfunc:true */
|
||||
/*eslint no-loop-func:0 */
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('noneditable', function(editor) {
|
||||
var TreeWalker = tinymce.dom.TreeWalker;
|
||||
var externalName = 'contenteditable', internalName = 'data-mce-' + externalName;
|
||||
var VK = tinymce.util.VK;
|
||||
|
||||
// Returns the content editable state of a node "true/false" or null
|
||||
function getContentEditable(node) {
|
||||
var contentEditable;
|
||||
|
||||
// Ignore non elements
|
||||
if (node.nodeType === 1) {
|
||||
// Check for fake content editable
|
||||
contentEditable = node.getAttribute(internalName);
|
||||
if (contentEditable && contentEditable !== "inherit") {
|
||||
return contentEditable;
|
||||
}
|
||||
|
||||
// Check for real content editable
|
||||
contentEditable = node.contentEditable;
|
||||
if (contentEditable !== "inherit") {
|
||||
return contentEditable;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Returns the noneditable parent or null if there is a editable before it or if it wasn't found
|
||||
function getNonEditableParent(node) {
|
||||
var state;
|
||||
|
||||
while (node) {
|
||||
state = getContentEditable(node);
|
||||
if (state) {
|
||||
return state === "false" ? node : null;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
function handleContentEditableSelection() {
|
||||
var dom = editor.dom, selection = editor.selection, caretContainerId = 'mce_noneditablecaret', invisibleChar = '\uFEFF';
|
||||
|
||||
// Get caret container parent for the specified node
|
||||
function getParentCaretContainer(node) {
|
||||
while (node) {
|
||||
if (node.id === caretContainerId) {
|
||||
return node;
|
||||
}
|
||||
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
// Finds the first text node in the specified node
|
||||
function findFirstTextNode(node) {
|
||||
var walker;
|
||||
|
||||
if (node) {
|
||||
walker = new TreeWalker(node, node);
|
||||
|
||||
for (node = walker.current(); node; node = walker.next()) {
|
||||
if (node.nodeType === 3) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert caret container before/after target or expand selection to include block
|
||||
function insertCaretContainerOrExpandToBlock(target, before) {
|
||||
var caretContainer, rng;
|
||||
|
||||
// Select block
|
||||
if (getContentEditable(target) === "false") {
|
||||
if (dom.isBlock(target)) {
|
||||
selection.select(target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rng = dom.createRng();
|
||||
|
||||
if (getContentEditable(target) === "true") {
|
||||
if (!target.firstChild) {
|
||||
target.appendChild(editor.getDoc().createTextNode('\u00a0'));
|
||||
}
|
||||
|
||||
target = target.firstChild;
|
||||
before = true;
|
||||
}
|
||||
|
||||
/*
|
||||
caretContainer = dom.create('span', {
|
||||
id: caretContainerId,
|
||||
'data-mce-bogus': true,
|
||||
style:'border: 1px solid red'
|
||||
}, invisibleChar);
|
||||
*/
|
||||
|
||||
caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true}, invisibleChar);
|
||||
|
||||
if (before) {
|
||||
target.parentNode.insertBefore(caretContainer, target);
|
||||
} else {
|
||||
dom.insertAfter(caretContainer, target);
|
||||
}
|
||||
|
||||
rng.setStart(caretContainer.firstChild, 1);
|
||||
rng.collapse(true);
|
||||
selection.setRng(rng);
|
||||
|
||||
return caretContainer;
|
||||
}
|
||||
|
||||
// Removes any caret container except the one we might be in
|
||||
function removeCaretContainer(caretContainer) {
|
||||
var rng, child, currentCaretContainer, lastContainer;
|
||||
|
||||
if (caretContainer) {
|
||||
rng = selection.getRng(true);
|
||||
rng.setStartBefore(caretContainer);
|
||||
rng.setEndBefore(caretContainer);
|
||||
|
||||
child = findFirstTextNode(caretContainer);
|
||||
if (child && child.nodeValue.charAt(0) == invisibleChar) {
|
||||
child = child.deleteData(0, 1);
|
||||
}
|
||||
|
||||
dom.remove(caretContainer, true);
|
||||
|
||||
selection.setRng(rng);
|
||||
} else {
|
||||
currentCaretContainer = getParentCaretContainer(selection.getStart());
|
||||
while ((caretContainer = dom.get(caretContainerId)) && caretContainer !== lastContainer) {
|
||||
if (currentCaretContainer !== caretContainer) {
|
||||
child = findFirstTextNode(caretContainer);
|
||||
if (child && child.nodeValue.charAt(0) == invisibleChar) {
|
||||
child = child.deleteData(0, 1);
|
||||
}
|
||||
|
||||
dom.remove(caretContainer, true);
|
||||
}
|
||||
|
||||
lastContainer = caretContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modifies the selection to include contentEditable false elements or insert caret containers
|
||||
function moveSelection() {
|
||||
var nonEditableStart, nonEditableEnd, isCollapsed, rng, element;
|
||||
|
||||
// Checks if there is any contents to the left/right side of caret returns the noneditable element or
|
||||
// any editable element if it finds one inside
|
||||
function hasSideContent(element, left) {
|
||||
var container, offset, walker, node, len;
|
||||
|
||||
container = rng.startContainer;
|
||||
offset = rng.startOffset;
|
||||
|
||||
// If endpoint is in middle of text node then expand to beginning/end of element
|
||||
if (container.nodeType == 3) {
|
||||
len = container.nodeValue.length;
|
||||
if ((offset > 0 && offset < len) || (left ? offset == len : offset === 0)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Can we resolve the node by index
|
||||
if (offset < container.childNodes.length) {
|
||||
// Browser represents caret position as the offset at the start of an element. When moving right
|
||||
// this is the element we are moving into so we consider our container to be child node at offset-1
|
||||
var pos = !left && offset > 0 ? offset - 1 : offset;
|
||||
container = container.childNodes[pos];
|
||||
if (container.hasChildNodes()) {
|
||||
container = container.firstChild;
|
||||
}
|
||||
} else {
|
||||
// If not then the caret is at the last position in it's container and the caret container
|
||||
// should be inserted after the noneditable element
|
||||
return !left ? element : null;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk left/right to look for contents
|
||||
walker = new TreeWalker(container, element);
|
||||
while ((node = walker[left ? 'prev' : 'next']())) {
|
||||
if (node.nodeType === 3 && node.nodeValue.length > 0) {
|
||||
return;
|
||||
} else if (getContentEditable(node) === "true") {
|
||||
// Found contentEditable=true element return this one to we can move the caret inside it
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
// Remove any existing caret containers
|
||||
removeCaretContainer();
|
||||
|
||||
// Get noneditable start/end elements
|
||||
isCollapsed = selection.isCollapsed();
|
||||
nonEditableStart = getNonEditableParent(selection.getStart());
|
||||
nonEditableEnd = getNonEditableParent(selection.getEnd());
|
||||
|
||||
// Is any fo the range endpoints noneditable
|
||||
if (nonEditableStart || nonEditableEnd) {
|
||||
rng = selection.getRng(true);
|
||||
|
||||
// If it's a caret selection then look left/right to see if we need to move the caret out side or expand
|
||||
if (isCollapsed) {
|
||||
nonEditableStart = nonEditableStart || nonEditableEnd;
|
||||
|
||||
if ((element = hasSideContent(nonEditableStart, true))) {
|
||||
// We have no contents to the left of the caret then insert a caret container before the noneditable element
|
||||
insertCaretContainerOrExpandToBlock(element, true);
|
||||
} else if ((element = hasSideContent(nonEditableStart, false))) {
|
||||
// We have no contents to the right of the caret then insert a caret container after the noneditable element
|
||||
insertCaretContainerOrExpandToBlock(element, false);
|
||||
} else {
|
||||
// We are in the middle of a noneditable so expand to select it
|
||||
selection.select(nonEditableStart);
|
||||
}
|
||||
} else {
|
||||
rng = selection.getRng(true);
|
||||
|
||||
// Expand selection to include start non editable element
|
||||
if (nonEditableStart) {
|
||||
rng.setStartBefore(nonEditableStart);
|
||||
}
|
||||
|
||||
// Expand selection to include end non editable element
|
||||
if (nonEditableEnd) {
|
||||
rng.setEndAfter(nonEditableEnd);
|
||||
}
|
||||
|
||||
selection.setRng(rng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleKey(e) {
|
||||
var keyCode = e.keyCode, nonEditableParent, caretContainer, startElement, endElement;
|
||||
|
||||
function getNonEmptyTextNodeSibling(node, prev) {
|
||||
while ((node = node[prev ? 'previousSibling' : 'nextSibling'])) {
|
||||
if (node.nodeType !== 3 || node.nodeValue.length > 0) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function positionCaretOnElement(element, start) {
|
||||
selection.select(element);
|
||||
selection.collapse(start);
|
||||
}
|
||||
|
||||
function canDelete(backspace) {
|
||||
var rng, container, offset, nonEditableParent;
|
||||
|
||||
function removeNodeIfNotParent(node) {
|
||||
var parent = container;
|
||||
|
||||
while (parent) {
|
||||
if (parent === node) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
dom.remove(node);
|
||||
moveSelection();
|
||||
}
|
||||
|
||||
function isNextPrevTreeNodeNonEditable() {
|
||||
var node, walker, nonEmptyElements = editor.schema.getNonEmptyElements();
|
||||
|
||||
walker = new tinymce.dom.TreeWalker(container, editor.getBody());
|
||||
while ((node = (backspace ? walker.prev() : walker.next()))) {
|
||||
// Found IMG/INPUT etc
|
||||
if (nonEmptyElements[node.nodeName.toLowerCase()]) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Found text node with contents
|
||||
if (node.nodeType === 3 && tinymce.trim(node.nodeValue).length > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Found non editable node
|
||||
if (getContentEditable(node) === "false") {
|
||||
removeNodeIfNotParent(node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the content node is within a non editable parent
|
||||
if (getNonEditableParent(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selection.isCollapsed()) {
|
||||
rng = selection.getRng(true);
|
||||
container = rng.startContainer;
|
||||
offset = rng.startOffset;
|
||||
container = getParentCaretContainer(container) || container;
|
||||
|
||||
// Is in noneditable parent
|
||||
if ((nonEditableParent = getNonEditableParent(container))) {
|
||||
removeNodeIfNotParent(nonEditableParent);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the caret is in the middle of a text node
|
||||
if (container.nodeType == 3 && (backspace ? offset > 0 : offset < container.nodeValue.length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resolve container index
|
||||
if (container.nodeType == 1) {
|
||||
container = container.childNodes[offset] || container;
|
||||
}
|
||||
|
||||
// Check if previous or next tree node is non editable then block the event
|
||||
if (isNextPrevTreeNodeNonEditable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
startElement = selection.getStart();
|
||||
endElement = selection.getEnd();
|
||||
|
||||
// Disable all key presses in contentEditable=false except delete or backspace
|
||||
nonEditableParent = getNonEditableParent(startElement) || getNonEditableParent(endElement);
|
||||
if (nonEditableParent && (keyCode < 112 || keyCode > 124) && keyCode != VK.DELETE && keyCode != VK.BACKSPACE) {
|
||||
// Is Ctrl+c, Ctrl+v or Ctrl+x then use default browser behavior
|
||||
if ((tinymce.isMac ? e.metaKey : e.ctrlKey) && (keyCode == 67 || keyCode == 88 || keyCode == 86)) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
// Arrow left/right select the element and collapse left/right
|
||||
if (keyCode == VK.LEFT || keyCode == VK.RIGHT) {
|
||||
var left = keyCode == VK.LEFT;
|
||||
// If a block element find previous or next element to position the caret
|
||||
if (editor.dom.isBlock(nonEditableParent)) {
|
||||
var targetElement = left ? nonEditableParent.previousSibling : nonEditableParent.nextSibling;
|
||||
var walker = new TreeWalker(targetElement, targetElement);
|
||||
var caretElement = left ? walker.prev() : walker.next();
|
||||
positionCaretOnElement(caretElement, !left);
|
||||
} else {
|
||||
positionCaretOnElement(nonEditableParent, left);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Is arrow left/right, backspace or delete
|
||||
if (keyCode == VK.LEFT || keyCode == VK.RIGHT || keyCode == VK.BACKSPACE || keyCode == VK.DELETE) {
|
||||
caretContainer = getParentCaretContainer(startElement);
|
||||
if (caretContainer) {
|
||||
// Arrow left or backspace
|
||||
if (keyCode == VK.LEFT || keyCode == VK.BACKSPACE) {
|
||||
nonEditableParent = getNonEmptyTextNodeSibling(caretContainer, true);
|
||||
|
||||
if (nonEditableParent && getContentEditable(nonEditableParent) === "false") {
|
||||
e.preventDefault();
|
||||
|
||||
if (keyCode == VK.LEFT) {
|
||||
positionCaretOnElement(nonEditableParent, true);
|
||||
} else {
|
||||
dom.remove(nonEditableParent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
removeCaretContainer(caretContainer);
|
||||
}
|
||||
}
|
||||
|
||||
// Arrow right or delete
|
||||
if (keyCode == VK.RIGHT || keyCode == VK.DELETE) {
|
||||
nonEditableParent = getNonEmptyTextNodeSibling(caretContainer);
|
||||
|
||||
if (nonEditableParent && getContentEditable(nonEditableParent) === "false") {
|
||||
e.preventDefault();
|
||||
|
||||
if (keyCode == VK.RIGHT) {
|
||||
positionCaretOnElement(nonEditableParent, false);
|
||||
} else {
|
||||
dom.remove(nonEditableParent);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
removeCaretContainer(caretContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((keyCode == VK.BACKSPACE || keyCode == VK.DELETE) && !canDelete(keyCode == VK.BACKSPACE)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.on('mousedown', function(e) {
|
||||
var node = editor.selection.getNode();
|
||||
|
||||
if (getContentEditable(node) === "false" && node == e.target) {
|
||||
// Expand selection on mouse down we can't block the default event since it's used for drag/drop
|
||||
moveSelection();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('mouseup keyup', moveSelection);
|
||||
editor.on('keydown', handleKey);
|
||||
}
|
||||
|
||||
var editClass, nonEditClass, nonEditableRegExps;
|
||||
|
||||
// Converts configured regexps to noneditable span items
|
||||
function convertRegExpsToNonEditable(e) {
|
||||
var i = nonEditableRegExps.length, content = e.content, cls = tinymce.trim(nonEditClass);
|
||||
|
||||
// Don't replace the variables when raw is used for example on undo/redo
|
||||
if (e.format == "raw") {
|
||||
return;
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
content = content.replace(nonEditableRegExps[i], function(match) {
|
||||
var args = arguments, index = args[args.length - 2];
|
||||
|
||||
// Is value inside an attribute then don't replace
|
||||
if (index > 0 && content.charAt(index - 1) == '"') {
|
||||
return match;
|
||||
}
|
||||
|
||||
return (
|
||||
'<span class="' + cls + '" data-mce-content="' + editor.dom.encode(args[0]) + '">' +
|
||||
editor.dom.encode(typeof(args[1]) === "string" ? args[1] : args[0]) + '</span>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
e.content = content;
|
||||
}
|
||||
|
||||
editClass = " " + tinymce.trim(editor.getParam("noneditable_editable_class", "mceEditable")) + " ";
|
||||
nonEditClass = " " + tinymce.trim(editor.getParam("noneditable_noneditable_class", "mceNonEditable")) + " ";
|
||||
|
||||
// Setup noneditable regexps array
|
||||
nonEditableRegExps = editor.getParam("noneditable_regexp");
|
||||
if (nonEditableRegExps && !nonEditableRegExps.length) {
|
||||
nonEditableRegExps = [nonEditableRegExps];
|
||||
}
|
||||
|
||||
editor.on('PreInit', function() {
|
||||
handleContentEditableSelection();
|
||||
|
||||
if (nonEditableRegExps) {
|
||||
editor.on('BeforeSetContent', convertRegExpsToNonEditable);
|
||||
}
|
||||
|
||||
// Apply contentEditable true/false on elements with the noneditable/editable classes
|
||||
editor.parser.addAttributeFilter('class', function(nodes) {
|
||||
var i = nodes.length, className, node;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
className = " " + node.attr("class") + " ";
|
||||
|
||||
if (className.indexOf(editClass) !== -1) {
|
||||
node.attr(internalName, "true");
|
||||
} else if (className.indexOf(nonEditClass) !== -1) {
|
||||
node.attr(internalName, "false");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remove internal name
|
||||
editor.serializer.addAttributeFilter(internalName, function(nodes) {
|
||||
var i = nodes.length, node;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
|
||||
if (nonEditableRegExps && node.attr('data-mce-content')) {
|
||||
node.name = "#text";
|
||||
node.type = 3;
|
||||
node.raw = true;
|
||||
node.value = node.attr('data-mce-content');
|
||||
} else {
|
||||
node.attr(externalName, null);
|
||||
node.attr(internalName, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Convert external name into internal name
|
||||
editor.parser.addAttributeFilter(externalName, function(nodes) {
|
||||
var i = nodes.length, node;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
node.attr(internalName, node.attr(externalName));
|
||||
node.attr(externalName, null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.on('drop', function(e) {
|
||||
if (getNonEditableParent(e.target)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("noneditable",function(a){function b(a){var b;if(1===a.nodeType){if(b=a.getAttribute(k),b&&"inherit"!==b)return b;if(b=a.contentEditable,"inherit"!==b)return b}return null}function c(a){for(var c;a;){if(c=b(a))return"false"===c?a:null;a=a.parentNode}}function d(){function d(a){for(;a;){if(a.id===n)return a;a=a.parentNode}}function e(a){var b;if(a)for(b=new i(a,a),a=b.current();a;a=b.next())if(3===a.nodeType)return a}function f(c,d){var e,f;return"false"===b(c)&&k.isBlock(c)?void m.select(c):(f=k.createRng(),"true"===b(c)&&(c.firstChild||c.appendChild(a.getDoc().createTextNode("\xa0")),c=c.firstChild,d=!0),e=k.create("span",{id:n,"data-mce-bogus":!0},o),d?c.parentNode.insertBefore(e,c):k.insertAfter(e,c),f.setStart(e.firstChild,1),f.collapse(!0),m.setRng(f),e)}function g(a){var b,c,f,g;if(a)b=m.getRng(!0),b.setStartBefore(a),b.setEndBefore(a),c=e(a),c&&c.nodeValue.charAt(0)==o&&(c=c.deleteData(0,1)),k.remove(a,!0),m.setRng(b);else for(f=d(m.getStart());(a=k.get(n))&&a!==g;)f!==a&&(c=e(a),c&&c.nodeValue.charAt(0)==o&&(c=c.deleteData(0,1)),k.remove(a,!0)),g=a}function h(){function a(a,c){var d,e,f,g,h;if(d=j.startContainer,e=j.startOffset,3==d.nodeType){if(h=d.nodeValue.length,e>0&&h>e||(c?e==h:0===e))return}else{if(!(e<d.childNodes.length))return c?null:a;var k=!c&&e>0?e-1:e;d=d.childNodes[k],d.hasChildNodes()&&(d=d.firstChild)}for(f=new i(d,a);g=f[c?"prev":"next"]();){if(3===g.nodeType&&g.nodeValue.length>0)return;if("true"===b(g))return g}return a}var d,e,h,j,k;g(),h=m.isCollapsed(),d=c(m.getStart()),e=c(m.getEnd()),(d||e)&&(j=m.getRng(!0),h?(d=d||e,(k=a(d,!0))?f(k,!0):(k=a(d,!1))?f(k,!1):m.select(d)):(j=m.getRng(!0),d&&j.setStartBefore(d),e&&j.setEndAfter(e),m.setRng(j)))}function j(e){function f(a,b){for(;a=a[b?"previousSibling":"nextSibling"];)if(3!==a.nodeType||a.nodeValue.length>0)return a}function j(a,b){m.select(a),m.collapse(b)}function n(e){function f(a){for(var b=j;b;){if(b===a)return;b=b.parentNode}k.remove(a),h()}function g(){var d,g,h=a.schema.getNonEmptyElements();for(g=new tinymce.dom.TreeWalker(j,a.getBody());(d=e?g.prev():g.next())&&!h[d.nodeName.toLowerCase()]&&!(3===d.nodeType&&tinymce.trim(d.nodeValue).length>0);)if("false"===b(d))return f(d),!0;return c(d)?!0:!1}var i,j,l,n;if(m.isCollapsed()){if(i=m.getRng(!0),j=i.startContainer,l=i.startOffset,j=d(j)||j,n=c(j))return f(n),!1;if(3==j.nodeType&&(e?l>0:l<j.nodeValue.length))return!0;if(1==j.nodeType&&(j=j.childNodes[l]||j),g())return!1}return!0}var o,p,q,r,s=e.keyCode;if(q=m.getStart(),r=m.getEnd(),o=c(q)||c(r),o&&(112>s||s>124)&&s!=l.DELETE&&s!=l.BACKSPACE){if((tinymce.isMac?e.metaKey:e.ctrlKey)&&(67==s||88==s||86==s))return;if(e.preventDefault(),s==l.LEFT||s==l.RIGHT){var t=s==l.LEFT;if(a.dom.isBlock(o)){var u=t?o.previousSibling:o.nextSibling,v=new i(u,u),w=t?v.prev():v.next();j(w,!t)}else j(o,t)}}else if(s==l.LEFT||s==l.RIGHT||s==l.BACKSPACE||s==l.DELETE){if(p=d(q)){if(s==l.LEFT||s==l.BACKSPACE)if(o=f(p,!0),o&&"false"===b(o)){if(e.preventDefault(),s!=l.LEFT)return void k.remove(o);j(o,!0)}else g(p);if(s==l.RIGHT||s==l.DELETE)if(o=f(p),o&&"false"===b(o)){if(e.preventDefault(),s!=l.RIGHT)return void k.remove(o);j(o,!1)}else g(p)}if((s==l.BACKSPACE||s==l.DELETE)&&!n(s==l.BACKSPACE))return e.preventDefault(),!1}}var k=a.dom,m=a.selection,n="mce_noneditablecaret",o="\ufeff";a.on("mousedown",function(c){var d=a.selection.getNode();"false"===b(d)&&d==c.target&&h()}),a.on("mouseup keyup",h),a.on("keydown",j)}function e(b){var c=h.length,d=b.content,e=tinymce.trim(g);if("raw"!=b.format){for(;c--;)d=d.replace(h[c],function(b){var c=arguments,f=c[c.length-2];return f>0&&'"'==d.charAt(f-1)?b:'<span class="'+e+'" data-mce-content="'+a.dom.encode(c[0])+'">'+a.dom.encode("string"==typeof c[1]?c[1]:c[0])+"</span>"});b.content=d}}var f,g,h,i=tinymce.dom.TreeWalker,j="contenteditable",k="data-mce-"+j,l=tinymce.util.VK;f=" "+tinymce.trim(a.getParam("noneditable_editable_class","mceEditable"))+" ",g=" "+tinymce.trim(a.getParam("noneditable_noneditable_class","mceNonEditable"))+" ",h=a.getParam("noneditable_regexp"),h&&!h.length&&(h=[h]),a.on("PreInit",function(){d(),h&&a.on("BeforeSetContent",e),a.parser.addAttributeFilter("class",function(a){for(var b,c,d=a.length;d--;)c=a[d],b=" "+c.attr("class")+" ",-1!==b.indexOf(f)?c.attr(k,"true"):-1!==b.indexOf(g)&&c.attr(k,"false")}),a.serializer.addAttributeFilter(k,function(a){for(var b,c=a.length;c--;)b=a[c],h&&b.attr("data-mce-content")?(b.name="#text",b.type=3,b.raw=!0,b.value=b.attr("data-mce-content")):(b.attr(j,null),b.attr(k,null))}),a.parser.addAttributeFilter(j,function(a){for(var b,c=a.length;c--;)b=a[c],b.attr(k,b.attr(j)),b.attr(j,null)})}),a.on("drop",function(a){c(a.target)&&a.preventDefault()})});
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('pagebreak', function(editor) {
|
||||
var pageBreakClass = 'mce-pagebreak', separatorHtml = editor.getParam('pagebreak_separator', '<!-- pagebreak -->');
|
||||
|
||||
var pageBreakSeparatorRegExp = new RegExp(separatorHtml.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g, function(a) {
|
||||
return '\\' + a;
|
||||
}), 'gi');
|
||||
|
||||
var pageBreakPlaceHolderHtml = '<img src="' + tinymce.Env.transparentSrc + '" class="' +
|
||||
pageBreakClass + '" data-mce-resize="false" />';
|
||||
|
||||
// Register commands
|
||||
editor.addCommand('mcePageBreak', function() {
|
||||
if (editor.settings.pagebreak_split_block) {
|
||||
editor.insertContent('<p>' + pageBreakPlaceHolderHtml + '</p>');
|
||||
} else {
|
||||
editor.insertContent(pageBreakPlaceHolderHtml);
|
||||
}
|
||||
});
|
||||
|
||||
// Register buttons
|
||||
editor.addButton('pagebreak', {
|
||||
title: 'Page break',
|
||||
cmd: 'mcePageBreak'
|
||||
});
|
||||
|
||||
editor.addMenuItem('pagebreak', {
|
||||
text: 'Page break',
|
||||
icon: 'pagebreak',
|
||||
cmd: 'mcePageBreak',
|
||||
context: 'insert'
|
||||
});
|
||||
|
||||
editor.on('ResolveName', function(e) {
|
||||
if (e.target.nodeName == 'IMG' && editor.dom.hasClass(e.target, pageBreakClass)) {
|
||||
e.name = 'pagebreak';
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('click', function(e) {
|
||||
e = e.target;
|
||||
|
||||
if (e.nodeName === 'IMG' && editor.dom.hasClass(e, pageBreakClass)) {
|
||||
editor.selection.select(e);
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('BeforeSetContent', function(e) {
|
||||
e.content = e.content.replace(pageBreakSeparatorRegExp, pageBreakPlaceHolderHtml);
|
||||
});
|
||||
|
||||
editor.on('PreInit', function() {
|
||||
editor.serializer.addNodeFilter('img', function(nodes) {
|
||||
var i = nodes.length, node, className;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
className = node.attr('class');
|
||||
if (className && className.indexOf('mce-pagebreak') !== -1) {
|
||||
// Replace parent block node if pagebreak_split_block is enabled
|
||||
var parentNode = node.parent;
|
||||
if (editor.schema.getBlockElements()[parentNode.name] && editor.settings.pagebreak_split_block) {
|
||||
parentNode.type = 3;
|
||||
parentNode.value = separatorHtml;
|
||||
parentNode.raw = true;
|
||||
node.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
node.type = 3;
|
||||
node.value = separatorHtml;
|
||||
node.raw = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("pagebreak",function(a){var b="mce-pagebreak",c=a.getParam("pagebreak_separator","<!-- pagebreak -->"),d=new RegExp(c.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,function(a){return"\\"+a}),"gi"),e='<img src="'+tinymce.Env.transparentSrc+'" class="'+b+'" data-mce-resize="false" />';a.addCommand("mcePageBreak",function(){a.insertContent(a.settings.pagebreak_split_block?"<p>"+e+"</p>":e)}),a.addButton("pagebreak",{title:"Page break",cmd:"mcePageBreak"}),a.addMenuItem("pagebreak",{text:"Page break",icon:"pagebreak",cmd:"mcePageBreak",context:"insert"}),a.on("ResolveName",function(c){"IMG"==c.target.nodeName&&a.dom.hasClass(c.target,b)&&(c.name="pagebreak")}),a.on("click",function(c){c=c.target,"IMG"===c.nodeName&&a.dom.hasClass(c,b)&&a.selection.select(c)}),a.on("BeforeSetContent",function(a){a.content=a.content.replace(d,e)}),a.on("PreInit",function(){a.serializer.addNodeFilter("img",function(b){for(var d,e,f=b.length;f--;)if(d=b[f],e=d.attr("class"),e&&-1!==e.indexOf("mce-pagebreak")){var g=d.parent;if(a.schema.getBlockElements()[g.name]&&a.settings.pagebreak_split_block){g.type=3,g.value=c,g.raw=!0,d.remove();continue}d.type=3,d.value=c,d.raw=!0}})})});
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('preview', function(editor) {
|
||||
var settings = editor.settings, sandbox = !tinymce.Env.ie;
|
||||
|
||||
editor.addCommand('mcePreview', function() {
|
||||
editor.windowManager.open({
|
||||
title: 'Preview',
|
||||
width : parseInt(editor.getParam("plugin_preview_width", "650"), 10),
|
||||
height : parseInt(editor.getParam("plugin_preview_height", "500"), 10),
|
||||
html: '<iframe src="javascript:\'\'" frameborder="0"' + (sandbox ? ' sandbox="allow-scripts"' : '') + '></iframe>',
|
||||
buttons: {
|
||||
text: 'Close',
|
||||
onclick: function() {
|
||||
this.parent().parent().close();
|
||||
}
|
||||
},
|
||||
onPostRender: function() {
|
||||
var previewHtml, headHtml = '';
|
||||
|
||||
headHtml += '<base href="' + editor.documentBaseURI.getURI() + '">';
|
||||
|
||||
tinymce.each(editor.contentCSS, function(url) {
|
||||
headHtml += '<link type="text/css" rel="stylesheet" href="' + editor.documentBaseURI.toAbsolute(url) + '">';
|
||||
});
|
||||
|
||||
var bodyId = settings.body_id || 'tinymce';
|
||||
if (bodyId.indexOf('=') != -1) {
|
||||
bodyId = editor.getParam('body_id', '', 'hash');
|
||||
bodyId = bodyId[editor.id] || bodyId;
|
||||
}
|
||||
|
||||
var bodyClass = settings.body_class || '';
|
||||
if (bodyClass.indexOf('=') != -1) {
|
||||
bodyClass = editor.getParam('body_class', '', 'hash');
|
||||
bodyClass = bodyClass[editor.id] || '';
|
||||
}
|
||||
|
||||
var dirAttr = editor.settings.directionality ? ' dir="' + editor.settings.directionality + '"' : '';
|
||||
|
||||
previewHtml = (
|
||||
'<!DOCTYPE html>' +
|
||||
'<html>' +
|
||||
'<head>' +
|
||||
headHtml +
|
||||
'</head>' +
|
||||
'<body id="' + bodyId + '" class="mce-content-body ' + bodyClass + '"' + dirAttr + '>' +
|
||||
editor.getContent() +
|
||||
'</body>' +
|
||||
'</html>'
|
||||
);
|
||||
|
||||
if (!sandbox) {
|
||||
// IE 6-11 doesn't support data uris on iframes
|
||||
// so I guess they will have to be less secure since we can't sandbox on those
|
||||
// TODO: Use sandbox if future versions of IE supports iframes with data: uris.
|
||||
var doc = this.getEl('body').firstChild.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(previewHtml);
|
||||
doc.close();
|
||||
} else {
|
||||
this.getEl('body').firstChild.src = 'data:text/html;charset=utf-8,' + encodeURIComponent(previewHtml);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
editor.addButton('preview', {
|
||||
title : 'Preview',
|
||||
cmd : 'mcePreview'
|
||||
});
|
||||
|
||||
editor.addMenuItem('preview', {
|
||||
text : 'Preview',
|
||||
cmd : 'mcePreview',
|
||||
context: 'view'
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("preview",function(a){var b=a.settings,c=!tinymce.Env.ie;a.addCommand("mcePreview",function(){a.windowManager.open({title:"Preview",width:parseInt(a.getParam("plugin_preview_width","650"),10),height:parseInt(a.getParam("plugin_preview_height","500"),10),html:'<iframe src="javascript:\'\'" frameborder="0"'+(c?' sandbox="allow-scripts"':"")+"></iframe>",buttons:{text:"Close",onclick:function(){this.parent().parent().close()}},onPostRender:function(){var d,e="";e+='<base href="'+a.documentBaseURI.getURI()+'">',tinymce.each(a.contentCSS,function(b){e+='<link type="text/css" rel="stylesheet" href="'+a.documentBaseURI.toAbsolute(b)+'">'});var f=b.body_id||"tinymce";-1!=f.indexOf("=")&&(f=a.getParam("body_id","","hash"),f=f[a.id]||f);var g=b.body_class||"";-1!=g.indexOf("=")&&(g=a.getParam("body_class","","hash"),g=g[a.id]||"");var h=a.settings.directionality?' dir="'+a.settings.directionality+'"':"";if(d="<!DOCTYPE html><html><head>"+e+'</head><body id="'+f+'" class="mce-content-body '+g+'"'+h+">"+a.getContent()+"</body></html>",c)this.getEl("body").firstChild.src="data:text/html;charset=utf-8,"+encodeURIComponent(d);else{var i=this.getEl("body").firstChild.contentWindow.document;i.open(),i.write(d),i.close()}}})}),a.addButton("preview",{title:"Preview",cmd:"mcePreview"}),a.addMenuItem("preview",{text:"Preview",cmd:"mcePreview",context:"view"})});
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('print', function(editor) {
|
||||
editor.addCommand('mcePrint', function() {
|
||||
editor.getWin().print();
|
||||
});
|
||||
|
||||
editor.addButton('print', {
|
||||
title: 'Print',
|
||||
cmd: 'mcePrint'
|
||||
});
|
||||
|
||||
editor.addShortcut('Ctrl+P', '', 'mcePrint');
|
||||
|
||||
editor.addMenuItem('print', {
|
||||
text: 'Print',
|
||||
cmd: 'mcePrint',
|
||||
icon: 'print',
|
||||
shortcut: 'Ctrl+P',
|
||||
context: 'file'
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("print",function(a){a.addCommand("mcePrint",function(){a.getWin().print()}),a.addButton("print",{title:"Print",cmd:"mcePrint"}),a.addShortcut("Ctrl+P","","mcePrint"),a.addMenuItem("print",{text:"Print",cmd:"mcePrint",icon:"print",shortcut:"Ctrl+P",context:"file"})});
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('save', function(editor) {
|
||||
function save() {
|
||||
var formObj;
|
||||
|
||||
formObj = tinymce.DOM.getParent(editor.id, 'form');
|
||||
|
||||
if (editor.getParam("save_enablewhendirty", true) && !editor.isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
tinymce.triggerSave();
|
||||
|
||||
// Use callback instead
|
||||
if (editor.getParam("save_onsavecallback")) {
|
||||
if (editor.execCallback('save_onsavecallback', editor)) {
|
||||
editor.startContent = tinymce.trim(editor.getContent({format: 'raw'}));
|
||||
editor.nodeChanged();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (formObj) {
|
||||
editor.isNotDirty = true;
|
||||
|
||||
if (!formObj.onsubmit || formObj.onsubmit()) {
|
||||
if (typeof(formObj.submit) == "function") {
|
||||
formObj.submit();
|
||||
} else {
|
||||
editor.windowManager.alert("Error: Form submit field collision.");
|
||||
}
|
||||
}
|
||||
|
||||
editor.nodeChanged();
|
||||
} else {
|
||||
editor.windowManager.alert("Error: No form element found.");
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
var h = tinymce.trim(editor.startContent);
|
||||
|
||||
// Use callback instead
|
||||
if (editor.getParam("save_oncancelcallback")) {
|
||||
editor.execCallback('save_oncancelcallback', editor);
|
||||
return;
|
||||
}
|
||||
|
||||
editor.setContent(h);
|
||||
editor.undoManager.clear();
|
||||
editor.nodeChanged();
|
||||
}
|
||||
|
||||
function stateToggle() {
|
||||
var self = this;
|
||||
|
||||
editor.on('nodeChange', function() {
|
||||
self.disabled(editor.getParam("save_enablewhendirty", true) && !editor.isDirty());
|
||||
});
|
||||
}
|
||||
|
||||
editor.addCommand('mceSave', save);
|
||||
editor.addCommand('mceCancel', cancel);
|
||||
|
||||
editor.addButton('save', {
|
||||
icon: 'save',
|
||||
text: 'Save',
|
||||
cmd: 'mceSave',
|
||||
disabled: true,
|
||||
onPostRender: stateToggle
|
||||
});
|
||||
|
||||
editor.addButton('cancel', {
|
||||
text: 'Cancel',
|
||||
icon: false,
|
||||
cmd: 'mceCancel',
|
||||
disabled: true,
|
||||
onPostRender: stateToggle
|
||||
});
|
||||
|
||||
editor.addShortcut('ctrl+s', '', 'mceSave');
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("save",function(a){function b(){var b;return b=tinymce.DOM.getParent(a.id,"form"),!a.getParam("save_enablewhendirty",!0)||a.isDirty()?(tinymce.triggerSave(),a.getParam("save_onsavecallback")?void(a.execCallback("save_onsavecallback",a)&&(a.startContent=tinymce.trim(a.getContent({format:"raw"})),a.nodeChanged())):void(b?(a.isNotDirty=!0,(!b.onsubmit||b.onsubmit())&&("function"==typeof b.submit?b.submit():a.windowManager.alert("Error: Form submit field collision.")),a.nodeChanged()):a.windowManager.alert("Error: No form element found."))):void 0}function c(){var b=tinymce.trim(a.startContent);return a.getParam("save_oncancelcallback")?void a.execCallback("save_oncancelcallback",a):(a.setContent(b),a.undoManager.clear(),void a.nodeChanged())}function d(){var b=this;a.on("nodeChange",function(){b.disabled(a.getParam("save_enablewhendirty",!0)&&!a.isDirty())})}a.addCommand("mceSave",b),a.addCommand("mceCancel",c),a.addButton("save",{icon:"save",text:"Save",cmd:"mceSave",disabled:!0,onPostRender:d}),a.addButton("cancel",{text:"Cancel",icon:!1,cmd:"mceCancel",disabled:!0,onPostRender:d}),a.addShortcut("ctrl+s","","mceSave")});
|
|
@ -0,0 +1,594 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*jshint smarttabs:true, undef:true, unused:true, latedef:true, curly:true, bitwise:true */
|
||||
/*eslint no-labels:0, no-constant-condition: 0 */
|
||||
/*global tinymce:true */
|
||||
|
||||
(function() {
|
||||
// Based on work developed by: James Padolsey http://james.padolsey.com
|
||||
// released under UNLICENSE that is compatible with LGPL
|
||||
// TODO: Handle contentEditable edgecase:
|
||||
// <p>text<span contentEditable="false">text<span contentEditable="true">text</span>text</span>text</p>
|
||||
function findAndReplaceDOMText(regex, node, replacementNode, captureGroup, schema) {
|
||||
var m, matches = [], text, count = 0, doc;
|
||||
var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
|
||||
|
||||
doc = node.ownerDocument;
|
||||
blockElementsMap = schema.getBlockElements(); // H1-H6, P, TD etc
|
||||
hiddenTextElementsMap = schema.getWhiteSpaceElements(); // TEXTAREA, PRE, STYLE, SCRIPT
|
||||
shortEndedElementsMap = schema.getShortEndedElements(); // BR, IMG, INPUT
|
||||
|
||||
function getMatchIndexes(m, captureGroup) {
|
||||
captureGroup = captureGroup || 0;
|
||||
|
||||
if (!m[0]) {
|
||||
throw 'findAndReplaceDOMText cannot handle zero-length matches';
|
||||
}
|
||||
|
||||
var index = m.index;
|
||||
|
||||
if (captureGroup > 0) {
|
||||
var cg = m[captureGroup];
|
||||
|
||||
if (!cg) {
|
||||
throw 'Invalid capture group';
|
||||
}
|
||||
|
||||
index += m[0].indexOf(cg);
|
||||
m[0] = cg;
|
||||
}
|
||||
|
||||
return [index, index + m[0].length, [m[0]]];
|
||||
}
|
||||
|
||||
function getText(node) {
|
||||
var txt;
|
||||
|
||||
if (node.nodeType === 3) {
|
||||
return node.data;
|
||||
}
|
||||
|
||||
if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
txt = '';
|
||||
|
||||
if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
|
||||
txt += '\n';
|
||||
}
|
||||
|
||||
if ((node = node.firstChild)) {
|
||||
do {
|
||||
txt += getText(node);
|
||||
} while ((node = node.nextSibling));
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
function stepThroughMatches(node, matches, replaceFn) {
|
||||
var startNode, endNode, startNodeIndex,
|
||||
endNodeIndex, innerNodes = [], atIndex = 0, curNode = node,
|
||||
matchLocation = matches.shift(), matchIndex = 0;
|
||||
|
||||
out: while (true) {
|
||||
if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName]) {
|
||||
atIndex++;
|
||||
}
|
||||
|
||||
if (curNode.nodeType === 3) {
|
||||
if (!endNode && curNode.length + atIndex >= matchLocation[1]) {
|
||||
// We've found the ending
|
||||
endNode = curNode;
|
||||
endNodeIndex = matchLocation[1] - atIndex;
|
||||
} else if (startNode) {
|
||||
// Intersecting node
|
||||
innerNodes.push(curNode);
|
||||
}
|
||||
|
||||
if (!startNode && curNode.length + atIndex > matchLocation[0]) {
|
||||
// We've found the match start
|
||||
startNode = curNode;
|
||||
startNodeIndex = matchLocation[0] - atIndex;
|
||||
}
|
||||
|
||||
atIndex += curNode.length;
|
||||
}
|
||||
|
||||
if (startNode && endNode) {
|
||||
curNode = replaceFn({
|
||||
startNode: startNode,
|
||||
startNodeIndex: startNodeIndex,
|
||||
endNode: endNode,
|
||||
endNodeIndex: endNodeIndex,
|
||||
innerNodes: innerNodes,
|
||||
match: matchLocation[2],
|
||||
matchIndex: matchIndex
|
||||
});
|
||||
|
||||
// replaceFn has to return the node that replaced the endNode
|
||||
// and then we step back so we can continue from the end of the
|
||||
// match:
|
||||
atIndex -= (endNode.length - endNodeIndex);
|
||||
startNode = null;
|
||||
endNode = null;
|
||||
innerNodes = [];
|
||||
matchLocation = matches.shift();
|
||||
matchIndex++;
|
||||
|
||||
if (!matchLocation) {
|
||||
break; // no more matches
|
||||
}
|
||||
} else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
|
||||
// Move down
|
||||
curNode = curNode.firstChild;
|
||||
continue;
|
||||
} else if (curNode.nextSibling) {
|
||||
// Move forward:
|
||||
curNode = curNode.nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Move forward or up:
|
||||
while (true) {
|
||||
if (curNode.nextSibling) {
|
||||
curNode = curNode.nextSibling;
|
||||
break;
|
||||
} else if (curNode.parentNode !== node) {
|
||||
curNode = curNode.parentNode;
|
||||
} else {
|
||||
break out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the actual replaceFn which splits up text nodes
|
||||
* and inserts the replacement element.
|
||||
*/
|
||||
function genReplacer(nodeName) {
|
||||
var makeReplacementNode;
|
||||
|
||||
if (typeof nodeName != 'function') {
|
||||
var stencilNode = nodeName.nodeType ? nodeName : doc.createElement(nodeName);
|
||||
|
||||
makeReplacementNode = function(fill, matchIndex) {
|
||||
var clone = stencilNode.cloneNode(false);
|
||||
|
||||
clone.setAttribute('data-mce-index', matchIndex);
|
||||
|
||||
if (fill) {
|
||||
clone.appendChild(doc.createTextNode(fill));
|
||||
}
|
||||
|
||||
return clone;
|
||||
};
|
||||
} else {
|
||||
makeReplacementNode = nodeName;
|
||||
}
|
||||
|
||||
return function(range) {
|
||||
var before, after, parentNode, startNode = range.startNode,
|
||||
endNode = range.endNode, matchIndex = range.matchIndex;
|
||||
|
||||
if (startNode === endNode) {
|
||||
var node = startNode;
|
||||
|
||||
parentNode = node.parentNode;
|
||||
if (range.startNodeIndex > 0) {
|
||||
// Add `before` text node (before the match)
|
||||
before = doc.createTextNode(node.data.substring(0, range.startNodeIndex));
|
||||
parentNode.insertBefore(before, node);
|
||||
}
|
||||
|
||||
// Create the replacement node:
|
||||
var el = makeReplacementNode(range.match[0], matchIndex);
|
||||
parentNode.insertBefore(el, node);
|
||||
if (range.endNodeIndex < node.length) {
|
||||
// Add `after` text node (after the match)
|
||||
after = doc.createTextNode(node.data.substring(range.endNodeIndex));
|
||||
parentNode.insertBefore(after, node);
|
||||
}
|
||||
|
||||
node.parentNode.removeChild(node);
|
||||
|
||||
return el;
|
||||
} else {
|
||||
// Replace startNode -> [innerNodes...] -> endNode (in that order)
|
||||
before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
|
||||
after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
|
||||
var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
|
||||
var innerEls = [];
|
||||
|
||||
for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
|
||||
var innerNode = range.innerNodes[i];
|
||||
var innerEl = makeReplacementNode(innerNode.data, matchIndex);
|
||||
innerNode.parentNode.replaceChild(innerEl, innerNode);
|
||||
innerEls.push(innerEl);
|
||||
}
|
||||
|
||||
var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
|
||||
|
||||
parentNode = startNode.parentNode;
|
||||
parentNode.insertBefore(before, startNode);
|
||||
parentNode.insertBefore(elA, startNode);
|
||||
parentNode.removeChild(startNode);
|
||||
|
||||
parentNode = endNode.parentNode;
|
||||
parentNode.insertBefore(elB, endNode);
|
||||
parentNode.insertBefore(after, endNode);
|
||||
parentNode.removeChild(endNode);
|
||||
|
||||
return elB;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
text = getText(node);
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (regex.global) {
|
||||
while ((m = regex.exec(text))) {
|
||||
matches.push(getMatchIndexes(m, captureGroup));
|
||||
}
|
||||
} else {
|
||||
m = text.match(regex);
|
||||
matches.push(getMatchIndexes(m, captureGroup));
|
||||
}
|
||||
|
||||
if (matches.length) {
|
||||
count = matches.length;
|
||||
stepThroughMatches(node, matches, genReplacer(replacementNode));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function Plugin(editor) {
|
||||
var self = this, currentIndex = -1;
|
||||
|
||||
function showDialog() {
|
||||
var last = {};
|
||||
|
||||
function updateButtonStates() {
|
||||
win.statusbar.find('#next').disabled(!findSpansByIndex(currentIndex + 1).length);
|
||||
win.statusbar.find('#prev').disabled(!findSpansByIndex(currentIndex - 1).length);
|
||||
}
|
||||
|
||||
function notFoundAlert() {
|
||||
tinymce.ui.MessageBox.alert('Could not find the specified string.', function() {
|
||||
win.find('#find')[0].focus();
|
||||
});
|
||||
}
|
||||
|
||||
var win = tinymce.ui.Factory.create({
|
||||
type: 'window',
|
||||
layout: "flex",
|
||||
pack: "center",
|
||||
align: "center",
|
||||
onClose: function() {
|
||||
editor.focus();
|
||||
self.done();
|
||||
},
|
||||
onSubmit: function(e) {
|
||||
var count, caseState, text, wholeWord;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
caseState = win.find('#case').checked();
|
||||
wholeWord = win.find('#words').checked();
|
||||
|
||||
text = win.find('#find').value();
|
||||
if (!text.length) {
|
||||
self.done(false);
|
||||
win.statusbar.items().slice(1).disabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (last.text == text && last.caseState == caseState && last.wholeWord == wholeWord) {
|
||||
if (findSpansByIndex(currentIndex + 1).length === 0) {
|
||||
notFoundAlert();
|
||||
return;
|
||||
}
|
||||
|
||||
self.next();
|
||||
updateButtonStates();
|
||||
return;
|
||||
}
|
||||
|
||||
count = self.find(text, caseState, wholeWord);
|
||||
if (!count) {
|
||||
notFoundAlert();
|
||||
}
|
||||
|
||||
win.statusbar.items().slice(1).disabled(count === 0);
|
||||
updateButtonStates();
|
||||
|
||||
last = {
|
||||
text: text,
|
||||
caseState: caseState,
|
||||
wholeWord: wholeWord
|
||||
};
|
||||
},
|
||||
buttons: [
|
||||
{text: "Find", onclick: function() {
|
||||
win.submit();
|
||||
}},
|
||||
{text: "Replace", disabled: true, onclick: function() {
|
||||
if (!self.replace(win.find('#replace').value())) {
|
||||
win.statusbar.items().slice(1).disabled(true);
|
||||
currentIndex = -1;
|
||||
last = {};
|
||||
}
|
||||
}},
|
||||
{text: "Replace all", disabled: true, onclick: function() {
|
||||
self.replace(win.find('#replace').value(), true, true);
|
||||
win.statusbar.items().slice(1).disabled(true);
|
||||
last = {};
|
||||
}},
|
||||
{type: "spacer", flex: 1},
|
||||
{text: "Prev", name: 'prev', disabled: true, onclick: function() {
|
||||
self.prev();
|
||||
updateButtonStates();
|
||||
}},
|
||||
{text: "Next", name: 'next', disabled: true, onclick: function() {
|
||||
self.next();
|
||||
updateButtonStates();
|
||||
}}
|
||||
],
|
||||
title: "Find and replace",
|
||||
items: {
|
||||
type: "form",
|
||||
padding: 20,
|
||||
labelGap: 30,
|
||||
spacing: 10,
|
||||
items: [
|
||||
{type: 'textbox', name: 'find', size: 40, label: 'Find', value: editor.selection.getNode().src},
|
||||
{type: 'textbox', name: 'replace', size: 40, label: 'Replace with'},
|
||||
{type: 'checkbox', name: 'case', text: 'Match case', label: ' '},
|
||||
{type: 'checkbox', name: 'words', text: 'Whole words', label: ' '}
|
||||
]
|
||||
}
|
||||
}).renderTo().reflow();
|
||||
}
|
||||
|
||||
self.init = function(ed) {
|
||||
ed.addMenuItem('searchreplace', {
|
||||
text: 'Find and replace',
|
||||
shortcut: 'Ctrl+F',
|
||||
onclick: showDialog,
|
||||
separator: 'before',
|
||||
context: 'edit'
|
||||
});
|
||||
|
||||
ed.addButton('searchreplace', {
|
||||
tooltip: 'Find and replace',
|
||||
shortcut: 'Ctrl+F',
|
||||
onclick: showDialog
|
||||
});
|
||||
|
||||
ed.addCommand("SearchReplace", showDialog);
|
||||
ed.shortcuts.add('Ctrl+F', '', showDialog);
|
||||
};
|
||||
|
||||
function getElmIndex(elm) {
|
||||
var value = elm.getAttribute('data-mce-index');
|
||||
|
||||
if (typeof(value) == "number") {
|
||||
return "" + value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function markAllMatches(regex) {
|
||||
var node, marker;
|
||||
|
||||
marker = editor.dom.create('span', {
|
||||
"data-mce-bogus": 1
|
||||
});
|
||||
|
||||
marker.className = 'mce-match-marker'; // IE 7 adds class="mce-match-marker" and class=mce-match-marker
|
||||
node = editor.getBody();
|
||||
|
||||
self.done(false);
|
||||
|
||||
return findAndReplaceDOMText(regex, node, marker, false, editor.schema);
|
||||
}
|
||||
|
||||
function unwrap(node) {
|
||||
var parentNode = node.parentNode;
|
||||
|
||||
if (node.firstChild) {
|
||||
parentNode.insertBefore(node.firstChild, node);
|
||||
}
|
||||
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
function findSpansByIndex(index) {
|
||||
var nodes, spans = [];
|
||||
|
||||
nodes = tinymce.toArray(editor.getBody().getElementsByTagName('span'));
|
||||
if (nodes.length) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var nodeIndex = getElmIndex(nodes[i]);
|
||||
|
||||
if (nodeIndex === null || !nodeIndex.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeIndex === index.toString()) {
|
||||
spans.push(nodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spans;
|
||||
}
|
||||
|
||||
function moveSelection(forward) {
|
||||
var testIndex = currentIndex, dom = editor.dom;
|
||||
|
||||
forward = forward !== false;
|
||||
|
||||
if (forward) {
|
||||
testIndex++;
|
||||
} else {
|
||||
testIndex--;
|
||||
}
|
||||
|
||||
dom.removeClass(findSpansByIndex(currentIndex), 'mce-match-marker-selected');
|
||||
|
||||
var spans = findSpansByIndex(testIndex);
|
||||
if (spans.length) {
|
||||
dom.addClass(findSpansByIndex(testIndex), 'mce-match-marker-selected');
|
||||
editor.selection.scrollIntoView(spans[0]);
|
||||
return testIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
function removeNode(node) {
|
||||
node.parentNode.removeChild(node);
|
||||
}
|
||||
|
||||
self.find = function(text, matchCase, wholeWord) {
|
||||
text = text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
text = wholeWord ? '\\b' + text + '\\b' : text;
|
||||
|
||||
var count = markAllMatches(new RegExp(text, matchCase ? 'g' : 'gi'));
|
||||
|
||||
if (count) {
|
||||
currentIndex = -1;
|
||||
currentIndex = moveSelection(true);
|
||||
}
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
self.next = function() {
|
||||
var index = moveSelection(true);
|
||||
|
||||
if (index !== -1) {
|
||||
currentIndex = index;
|
||||
}
|
||||
};
|
||||
|
||||
self.prev = function() {
|
||||
var index = moveSelection(false);
|
||||
|
||||
if (index !== -1) {
|
||||
currentIndex = index;
|
||||
}
|
||||
};
|
||||
|
||||
self.replace = function(text, forward, all) {
|
||||
var i, nodes, node, matchIndex, currentMatchIndex, nextIndex = currentIndex, hasMore;
|
||||
|
||||
forward = forward !== false;
|
||||
|
||||
node = editor.getBody();
|
||||
nodes = tinymce.toArray(node.getElementsByTagName('span'));
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
var nodeIndex = getElmIndex(nodes[i]);
|
||||
|
||||
if (nodeIndex === null || !nodeIndex.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matchIndex = currentMatchIndex = parseInt(nodeIndex, 10);
|
||||
if (all || matchIndex === currentIndex) {
|
||||
if (text.length) {
|
||||
nodes[i].firstChild.nodeValue = text;
|
||||
unwrap(nodes[i]);
|
||||
} else {
|
||||
removeNode(nodes[i]);
|
||||
}
|
||||
|
||||
while (nodes[++i]) {
|
||||
matchIndex = getElmIndex(nodes[i]);
|
||||
|
||||
if (nodeIndex === null || !nodeIndex.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (matchIndex === currentMatchIndex) {
|
||||
removeNode(nodes[i]);
|
||||
} else {
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (forward) {
|
||||
nextIndex--;
|
||||
}
|
||||
} else if (currentMatchIndex > currentIndex) {
|
||||
nodes[i].setAttribute('data-mce-index', currentMatchIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
editor.undoManager.add();
|
||||
currentIndex = nextIndex;
|
||||
|
||||
if (forward) {
|
||||
hasMore = findSpansByIndex(nextIndex + 1).length > 0;
|
||||
self.next();
|
||||
} else {
|
||||
hasMore = findSpansByIndex(nextIndex - 1).length > 0;
|
||||
self.prev();
|
||||
}
|
||||
|
||||
return !all && hasMore;
|
||||
};
|
||||
|
||||
self.done = function(keepEditorSelection) {
|
||||
var i, nodes, startContainer, endContainer;
|
||||
|
||||
nodes = tinymce.toArray(editor.getBody().getElementsByTagName('span'));
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
var nodeIndex = getElmIndex(nodes[i]);
|
||||
|
||||
if (nodeIndex !== null && nodeIndex.length) {
|
||||
if (nodeIndex === currentIndex.toString()) {
|
||||
if (!startContainer) {
|
||||
startContainer = nodes[i].firstChild;
|
||||
}
|
||||
|
||||
endContainer = nodes[i].firstChild;
|
||||
}
|
||||
|
||||
unwrap(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (startContainer && endContainer) {
|
||||
var rng = editor.dom.createRng();
|
||||
rng.setStart(startContainer, 0);
|
||||
rng.setEnd(endContainer, endContainer.data.length);
|
||||
|
||||
if (keepEditorSelection !== false) {
|
||||
editor.selection.setRng(rng);
|
||||
}
|
||||
|
||||
return rng;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tinymce.PluginManager.add('searchreplace', Plugin);
|
||||
})();
|
|
@ -0,0 +1,996 @@
|
|||
/**
|
||||
* Compiled inline version. (Library mode)
|
||||
*/
|
||||
|
||||
/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
|
||||
/*globals $code */
|
||||
|
||||
(function(exports, undefined) {
|
||||
"use strict";
|
||||
|
||||
var modules = {};
|
||||
|
||||
function require(ids, callback) {
|
||||
var module, defs = [];
|
||||
|
||||
for (var i = 0; i < ids.length; ++i) {
|
||||
module = modules[ids[i]] || resolve(ids[i]);
|
||||
if (!module) {
|
||||
throw 'module definition dependecy not found: ' + ids[i];
|
||||
}
|
||||
|
||||
defs.push(module);
|
||||
}
|
||||
|
||||
callback.apply(null, defs);
|
||||
}
|
||||
|
||||
function define(id, dependencies, definition) {
|
||||
if (typeof id !== 'string') {
|
||||
throw 'invalid module definition, module id must be defined and be a string';
|
||||
}
|
||||
|
||||
if (dependencies === undefined) {
|
||||
throw 'invalid module definition, dependencies must be specified';
|
||||
}
|
||||
|
||||
if (definition === undefined) {
|
||||
throw 'invalid module definition, definition function must be specified';
|
||||
}
|
||||
|
||||
require(dependencies, function() {
|
||||
modules[id] = definition.apply(null, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
function defined(id) {
|
||||
return !!modules[id];
|
||||
}
|
||||
|
||||
function resolve(id) {
|
||||
var target = exports;
|
||||
var fragments = id.split(/[.\/]/);
|
||||
|
||||
for (var fi = 0; fi < fragments.length; ++fi) {
|
||||
if (!target[fragments[fi]]) {
|
||||
return;
|
||||
}
|
||||
|
||||
target = target[fragments[fi]];
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function expose(ids) {
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var target = exports;
|
||||
var id = ids[i];
|
||||
var fragments = id.split(/[.\/]/);
|
||||
|
||||
for (var fi = 0; fi < fragments.length - 1; ++fi) {
|
||||
if (target[fragments[fi]] === undefined) {
|
||||
target[fragments[fi]] = {};
|
||||
}
|
||||
|
||||
target = target[fragments[fi]];
|
||||
}
|
||||
|
||||
target[fragments[fragments.length - 1]] = modules[id];
|
||||
}
|
||||
}
|
||||
|
||||
// Included from: js/tinymce/plugins/spellchecker/classes/DomTextMatcher.js
|
||||
|
||||
/**
|
||||
* DomTextMatcher.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*eslint no-labels:0, no-constant-condition: 0 */
|
||||
|
||||
/**
|
||||
* This class logic for filtering text and matching words.
|
||||
*
|
||||
* @class tinymce.spellcheckerplugin.TextFilter
|
||||
* @private
|
||||
*/
|
||||
define("tinymce/spellcheckerplugin/DomTextMatcher", [], function() {
|
||||
// Based on work developed by: James Padolsey http://james.padolsey.com
|
||||
// released under UNLICENSE that is compatible with LGPL
|
||||
// TODO: Handle contentEditable edgecase:
|
||||
// <p>text<span contentEditable="false">text<span contentEditable="true">text</span>text</span>text</p>
|
||||
return function(node, editor) {
|
||||
var m, matches = [], text, dom = editor.dom;
|
||||
var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
|
||||
|
||||
blockElementsMap = editor.schema.getBlockElements(); // H1-H6, P, TD etc
|
||||
hiddenTextElementsMap = editor.schema.getWhiteSpaceElements(); // TEXTAREA, PRE, STYLE, SCRIPT
|
||||
shortEndedElementsMap = editor.schema.getShortEndedElements(); // BR, IMG, INPUT
|
||||
|
||||
function createMatch(m, data) {
|
||||
if (!m[0]) {
|
||||
throw 'findAndReplaceDOMText cannot handle zero-length matches';
|
||||
}
|
||||
|
||||
return {
|
||||
start: m.index,
|
||||
end: m.index + m[0].length,
|
||||
text: m[0],
|
||||
data: data
|
||||
};
|
||||
}
|
||||
|
||||
function getText(node) {
|
||||
var txt;
|
||||
|
||||
if (node.nodeType === 3) {
|
||||
return node.data;
|
||||
}
|
||||
|
||||
if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
|
||||
return '';
|
||||
}
|
||||
|
||||
txt = '';
|
||||
|
||||
if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
|
||||
txt += '\n';
|
||||
}
|
||||
|
||||
if ((node = node.firstChild)) {
|
||||
do {
|
||||
txt += getText(node);
|
||||
} while ((node = node.nextSibling));
|
||||
}
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
function stepThroughMatches(node, matches, replaceFn) {
|
||||
var startNode, endNode, startNodeIndex,
|
||||
endNodeIndex, innerNodes = [], atIndex = 0, curNode = node,
|
||||
matchLocation, matchIndex = 0;
|
||||
|
||||
matches = matches.slice(0);
|
||||
matches.sort(function(a, b) {
|
||||
return a.start - b.start;
|
||||
});
|
||||
|
||||
matchLocation = matches.shift();
|
||||
|
||||
out: while (true) {
|
||||
if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName]) {
|
||||
atIndex++;
|
||||
}
|
||||
|
||||
if (curNode.nodeType === 3) {
|
||||
if (!endNode && curNode.length + atIndex >= matchLocation.end) {
|
||||
// We've found the ending
|
||||
endNode = curNode;
|
||||
endNodeIndex = matchLocation.end - atIndex;
|
||||
} else if (startNode) {
|
||||
// Intersecting node
|
||||
innerNodes.push(curNode);
|
||||
}
|
||||
|
||||
if (!startNode && curNode.length + atIndex > matchLocation.start) {
|
||||
// We've found the match start
|
||||
startNode = curNode;
|
||||
startNodeIndex = matchLocation.start - atIndex;
|
||||
}
|
||||
|
||||
atIndex += curNode.length;
|
||||
}
|
||||
|
||||
if (startNode && endNode) {
|
||||
curNode = replaceFn({
|
||||
startNode: startNode,
|
||||
startNodeIndex: startNodeIndex,
|
||||
endNode: endNode,
|
||||
endNodeIndex: endNodeIndex,
|
||||
innerNodes: innerNodes,
|
||||
match: matchLocation.text,
|
||||
matchIndex: matchIndex
|
||||
});
|
||||
|
||||
// replaceFn has to return the node that replaced the endNode
|
||||
// and then we step back so we can continue from the end of the
|
||||
// match:
|
||||
atIndex -= (endNode.length - endNodeIndex);
|
||||
startNode = null;
|
||||
endNode = null;
|
||||
innerNodes = [];
|
||||
matchLocation = matches.shift();
|
||||
matchIndex++;
|
||||
|
||||
if (!matchLocation) {
|
||||
break; // no more matches
|
||||
}
|
||||
} else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
|
||||
// Move down
|
||||
curNode = curNode.firstChild;
|
||||
continue;
|
||||
} else if (curNode.nextSibling) {
|
||||
// Move forward:
|
||||
curNode = curNode.nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Move forward or up:
|
||||
while (true) {
|
||||
if (curNode.nextSibling) {
|
||||
curNode = curNode.nextSibling;
|
||||
break;
|
||||
} else if (curNode.parentNode !== node) {
|
||||
curNode = curNode.parentNode;
|
||||
} else {
|
||||
break out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the actual replaceFn which splits up text nodes
|
||||
* and inserts the replacement element.
|
||||
*/
|
||||
function genReplacer(callback) {
|
||||
function makeReplacementNode(fill, matchIndex) {
|
||||
var match = matches[matchIndex];
|
||||
|
||||
if (!match.stencil) {
|
||||
match.stencil = callback(match);
|
||||
}
|
||||
|
||||
var clone = match.stencil.cloneNode(false);
|
||||
clone.setAttribute('data-mce-index', matchIndex);
|
||||
|
||||
if (fill) {
|
||||
clone.appendChild(dom.doc.createTextNode(fill));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return function(range) {
|
||||
var before, after, parentNode, startNode = range.startNode,
|
||||
endNode = range.endNode, matchIndex = range.matchIndex,
|
||||
doc = dom.doc;
|
||||
|
||||
if (startNode === endNode) {
|
||||
var node = startNode;
|
||||
|
||||
parentNode = node.parentNode;
|
||||
if (range.startNodeIndex > 0) {
|
||||
// Add "before" text node (before the match)
|
||||
before = doc.createTextNode(node.data.substring(0, range.startNodeIndex));
|
||||
parentNode.insertBefore(before, node);
|
||||
}
|
||||
|
||||
// Create the replacement node:
|
||||
var el = makeReplacementNode(range.match, matchIndex);
|
||||
parentNode.insertBefore(el, node);
|
||||
if (range.endNodeIndex < node.length) {
|
||||
// Add "after" text node (after the match)
|
||||
after = doc.createTextNode(node.data.substring(range.endNodeIndex));
|
||||
parentNode.insertBefore(after, node);
|
||||
}
|
||||
|
||||
node.parentNode.removeChild(node);
|
||||
|
||||
return el;
|
||||
} else {
|
||||
// Replace startNode -> [innerNodes...] -> endNode (in that order)
|
||||
before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
|
||||
after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
|
||||
var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
|
||||
var innerEls = [];
|
||||
|
||||
for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
|
||||
var innerNode = range.innerNodes[i];
|
||||
var innerEl = makeReplacementNode(innerNode.data, matchIndex);
|
||||
innerNode.parentNode.replaceChild(innerEl, innerNode);
|
||||
innerEls.push(innerEl);
|
||||
}
|
||||
|
||||
var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
|
||||
|
||||
parentNode = startNode.parentNode;
|
||||
parentNode.insertBefore(before, startNode);
|
||||
parentNode.insertBefore(elA, startNode);
|
||||
parentNode.removeChild(startNode);
|
||||
|
||||
parentNode = endNode.parentNode;
|
||||
parentNode.insertBefore(elB, endNode);
|
||||
parentNode.insertBefore(after, endNode);
|
||||
parentNode.removeChild(endNode);
|
||||
|
||||
return elB;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function unwrapElement(element) {
|
||||
var parentNode = element.parentNode;
|
||||
parentNode.insertBefore(element.firstChild, element);
|
||||
element.parentNode.removeChild(element);
|
||||
}
|
||||
|
||||
function getWrappersByIndex(index) {
|
||||
var elements = node.getElementsByTagName('*'), wrappers = [];
|
||||
|
||||
index = typeof(index) == "number" ? "" + index : null;
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i], dataIndex = element.getAttribute('data-mce-index');
|
||||
|
||||
if (dataIndex !== null && dataIndex.length) {
|
||||
if (dataIndex === index || index === null) {
|
||||
wrappers.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of a specific match object or -1 if it isn't found.
|
||||
*
|
||||
* @param {Match} match Text match object.
|
||||
* @return {Number} Index of match or -1 if it isn't found.
|
||||
*/
|
||||
function indexOf(match) {
|
||||
var i = matches.length;
|
||||
while (i--) {
|
||||
if (matches[i] === match) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the matches. If the callback returns true it stays if not it gets removed.
|
||||
*
|
||||
* @param {Function} callback Callback to execute for each match.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function filter(callback) {
|
||||
var filteredMatches = [];
|
||||
|
||||
each(function(match, i) {
|
||||
if (callback(match, i)) {
|
||||
filteredMatches.push(match);
|
||||
}
|
||||
});
|
||||
|
||||
matches = filteredMatches;
|
||||
|
||||
/*jshint validthis:true*/
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified callback for each match.
|
||||
*
|
||||
* @param {Function} callback Callback to execute for each match.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function each(callback) {
|
||||
for (var i = 0, l = matches.length; i < l; i++) {
|
||||
if (callback(matches[i], i) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*jshint validthis:true*/
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the current matches with nodes created by the specified callback.
|
||||
* Multiple clones of these matches might occur on matches that are on multiple nodex.
|
||||
*
|
||||
* @param {Function} callback Callback to execute in order to create elements for matches.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function wrap(callback) {
|
||||
if (matches.length) {
|
||||
stepThroughMatches(node, matches, genReplacer(callback));
|
||||
}
|
||||
|
||||
/*jshint validthis:true*/
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the specified regexp and adds them to the matches collection.
|
||||
*
|
||||
* @param {RegExp} regex Global regexp to search the current node by.
|
||||
* @param {Object} [data] Optional custom data element for the match.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function find(regex, data) {
|
||||
if (text && regex.global) {
|
||||
while ((m = regex.exec(text))) {
|
||||
matches.push(createMatch(m, data));
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps the specified match object or all matches if unspecified.
|
||||
*
|
||||
* @param {Object} [match] Optional match object.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function unwrap(match) {
|
||||
var i, elements = getWrappersByIndex(match ? indexOf(match) : null);
|
||||
|
||||
i = elements.length;
|
||||
while (i--) {
|
||||
unwrapElement(elements[i]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a match object by the specified DOM element.
|
||||
*
|
||||
* @param {DOMElement} element Element to return match object for.
|
||||
* @return {Object} Match object for the specified element.
|
||||
*/
|
||||
function matchFromElement(element) {
|
||||
return matches[element.getAttribute('data-mce-index')];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DOM element from the specified match element. This will be the first element if it's split
|
||||
* on multiple nodes.
|
||||
*
|
||||
* @param {Object} match Match element to get first element of.
|
||||
* @return {DOMElement} DOM element for the specified match object.
|
||||
*/
|
||||
function elementFromMatch(match) {
|
||||
return getWrappersByIndex(indexOf(match))[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds match the specified range for example a grammar line.
|
||||
*
|
||||
* @param {Number} start Start offset.
|
||||
* @param {Number} length Length of the text.
|
||||
* @param {Object} data Custom data object for match.
|
||||
* @return {DomTextMatcher} Current DomTextMatcher instance.
|
||||
*/
|
||||
function add(start, length, data) {
|
||||
matches.push({
|
||||
start: start,
|
||||
end: start + length,
|
||||
text: text.substr(start, length),
|
||||
data: data
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DOM range for the specified match.
|
||||
*
|
||||
* @param {Object} match Match object to get range for.
|
||||
* @return {DOMRange} DOM Range for the specified match.
|
||||
*/
|
||||
function rangeFromMatch(match) {
|
||||
var wrappers = getWrappersByIndex(indexOf(match));
|
||||
|
||||
var rng = editor.dom.createRng();
|
||||
rng.setStartBefore(wrappers[0]);
|
||||
rng.setEndAfter(wrappers[wrappers.length - 1]);
|
||||
|
||||
return rng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the specified match with the specified text.
|
||||
*
|
||||
* @param {Object} match Match object to replace.
|
||||
* @param {String} text Text to replace the match with.
|
||||
* @return {DOMRange} DOM range produced after the replace.
|
||||
*/
|
||||
function replace(match, text) {
|
||||
var rng = rangeFromMatch(match);
|
||||
|
||||
rng.deleteContents();
|
||||
|
||||
if (text.length > 0) {
|
||||
rng.insertNode(editor.dom.doc.createTextNode(text));
|
||||
}
|
||||
|
||||
return rng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the DomTextMatcher instance. This will remove any wrapped nodes and remove any matches.
|
||||
*
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
function reset() {
|
||||
matches.splice(0, matches.length);
|
||||
unwrap();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
text = getText(node);
|
||||
|
||||
return {
|
||||
text: text,
|
||||
matches: matches,
|
||||
each: each,
|
||||
filter: filter,
|
||||
reset: reset,
|
||||
matchFromElement: matchFromElement,
|
||||
elementFromMatch: elementFromMatch,
|
||||
find: find,
|
||||
add: add,
|
||||
wrap: wrap,
|
||||
unwrap: unwrap,
|
||||
replace: replace,
|
||||
rangeFromMatch: rangeFromMatch,
|
||||
indexOf: indexOf
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
// Included from: js/tinymce/plugins/spellchecker/classes/Plugin.js
|
||||
|
||||
/**
|
||||
* Plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*jshint camelcase:false */
|
||||
|
||||
/**
|
||||
* This class contains all core logic for the spellchecker plugin.
|
||||
*
|
||||
* @class tinymce.spellcheckerplugin.Plugin
|
||||
* @private
|
||||
*/
|
||||
define("tinymce/spellcheckerplugin/Plugin", [
|
||||
"tinymce/spellcheckerplugin/DomTextMatcher",
|
||||
"tinymce/PluginManager",
|
||||
"tinymce/util/Tools",
|
||||
"tinymce/ui/Menu",
|
||||
"tinymce/dom/DOMUtils",
|
||||
"tinymce/util/XHR",
|
||||
"tinymce/util/URI",
|
||||
"tinymce/util/JSON"
|
||||
], function(DomTextMatcher, PluginManager, Tools, Menu, DOMUtils, XHR, URI, JSON) {
|
||||
PluginManager.add('spellchecker', function(editor, url) {
|
||||
var languageMenuItems, self = this, lastSuggestions, started, suggestionsMenu, settings = editor.settings;
|
||||
var hasDictionarySupport;
|
||||
|
||||
function getTextMatcher() {
|
||||
if (!self.textMatcher) {
|
||||
self.textMatcher = new DomTextMatcher(editor.getBody(), editor);
|
||||
}
|
||||
|
||||
return self.textMatcher;
|
||||
}
|
||||
|
||||
function buildMenuItems(listName, languageValues) {
|
||||
var items = [];
|
||||
|
||||
Tools.each(languageValues, function(languageValue) {
|
||||
items.push({
|
||||
selectable: true,
|
||||
text: languageValue.name,
|
||||
data: languageValue.value
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
var languagesString = settings.spellchecker_languages ||
|
||||
'English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr_FR,' +
|
||||
'German=de,Italian=it,Polish=pl,Portuguese=pt_BR,' +
|
||||
'Spanish=es,Swedish=sv';
|
||||
|
||||
languageMenuItems = buildMenuItems('Language',
|
||||
Tools.map(languagesString.split(','), function(langPair) {
|
||||
langPair = langPair.split('=');
|
||||
|
||||
return {
|
||||
name: langPair[0],
|
||||
value: langPair[1]
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
function isEmpty(obj) {
|
||||
/*jshint unused:false*/
|
||||
/*eslint no-unused-vars:0 */
|
||||
for (var name in obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function showSuggestions(word, spans) {
|
||||
var items = [], suggestions = lastSuggestions[word];
|
||||
|
||||
Tools.each(suggestions, function(suggestion) {
|
||||
items.push({
|
||||
text: suggestion,
|
||||
onclick: function() {
|
||||
editor.insertContent(editor.dom.encode(suggestion));
|
||||
editor.dom.remove(spans);
|
||||
checkIfFinished();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
items.push({text: '-'});
|
||||
|
||||
if (hasDictionarySupport) {
|
||||
items.push({text: 'Add to Dictionary', onclick: function() {
|
||||
addToDictionary(word, spans);
|
||||
}});
|
||||
}
|
||||
|
||||
items.push.apply(items, [
|
||||
{text: 'Ignore', onclick: function() {
|
||||
ignoreWord(word, spans);
|
||||
}},
|
||||
|
||||
{text: 'Ignore all', onclick: function() {
|
||||
ignoreWord(word, spans, true);
|
||||
}}
|
||||
]);
|
||||
|
||||
// Render menu
|
||||
suggestionsMenu = new Menu({
|
||||
items: items,
|
||||
context: 'contextmenu',
|
||||
onautohide: function(e) {
|
||||
if (e.target.className.indexOf('spellchecker') != -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
onhide: function() {
|
||||
suggestionsMenu.remove();
|
||||
suggestionsMenu = null;
|
||||
}
|
||||
});
|
||||
|
||||
suggestionsMenu.renderTo(document.body);
|
||||
|
||||
// Position menu
|
||||
var pos = DOMUtils.DOM.getPos(editor.getContentAreaContainer());
|
||||
var targetPos = editor.dom.getPos(spans[0]);
|
||||
var root = editor.dom.getRoot();
|
||||
|
||||
// Adjust targetPos for scrolling in the editor
|
||||
if (root.nodeName == 'BODY') {
|
||||
targetPos.x -= root.ownerDocument.documentElement.scrollLeft || root.scrollLeft;
|
||||
targetPos.y -= root.ownerDocument.documentElement.scrollTop || root.scrollTop;
|
||||
} else {
|
||||
targetPos.x -= root.scrollLeft;
|
||||
targetPos.y -= root.scrollTop;
|
||||
}
|
||||
|
||||
pos.x += targetPos.x;
|
||||
pos.y += targetPos.y;
|
||||
|
||||
suggestionsMenu.moveTo(pos.x, pos.y + spans[0].offsetHeight);
|
||||
}
|
||||
|
||||
function getWordCharPattern() {
|
||||
// Regexp for finding word specific characters this will split words by
|
||||
// spaces, quotes, copy right characters etc. It's escaped with unicode characters
|
||||
// to make it easier to output scripts on servers using different encodings
|
||||
// so if you add any characters outside the 128 byte range make sure to escape it
|
||||
return editor.getParam('spellchecker_wordchar_pattern') || new RegExp("[^" +
|
||||
"\\s!\"#$%&()*+,-./:;<=>?@[\\]^_{|}`" +
|
||||
"\u00a7\u00a9\u00ab\u00ae\u00b1\u00b6\u00b7\u00b8\u00bb" +
|
||||
"\u00bc\u00bd\u00be\u00bf\u00d7\u00f7\u00a4\u201d\u201c\u201e\u00a0\u2002\u2003\u2009" +
|
||||
"]+", "g");
|
||||
}
|
||||
|
||||
function defaultSpellcheckCallback(method, text, doneCallback, errorCallback) {
|
||||
var data = {method: method}, postData = '';
|
||||
|
||||
if (method == "spellcheck") {
|
||||
data.text = text;
|
||||
data.lang = settings.spellchecker_language;
|
||||
}
|
||||
|
||||
if (method == "addToDictionary") {
|
||||
data.word = text;
|
||||
}
|
||||
|
||||
Tools.each(data, function(value, key) {
|
||||
if (postData) {
|
||||
postData += '&';
|
||||
}
|
||||
|
||||
postData += key + '=' + encodeURIComponent(value);
|
||||
});
|
||||
|
||||
XHR.send({
|
||||
url: new URI(url).toAbsolute(settings.spellchecker_rpc_url),
|
||||
type: "post",
|
||||
content_type: 'application/x-www-form-urlencoded',
|
||||
data: postData,
|
||||
success: function(result) {
|
||||
result = JSON.parse(result);
|
||||
|
||||
if (!result) {
|
||||
errorCallback("Sever response wasn't proper JSON.");
|
||||
} else if (result.error) {
|
||||
errorCallback(result.error);
|
||||
} else {
|
||||
doneCallback(result);
|
||||
}
|
||||
},
|
||||
error: function(type, xhr) {
|
||||
errorCallback("Spellchecker request error: " + xhr.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendRpcCall(name, data, successCallback, errorCallback) {
|
||||
var spellCheckCallback = settings.spellchecker_callback || defaultSpellcheckCallback;
|
||||
spellCheckCallback.call(self, name, data, successCallback, errorCallback);
|
||||
}
|
||||
|
||||
function spellcheck() {
|
||||
if (started) {
|
||||
finish();
|
||||
return;
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
function errorCallback(message) {
|
||||
editor.windowManager.alert(message);
|
||||
editor.setProgressState(false);
|
||||
finish();
|
||||
}
|
||||
|
||||
editor.setProgressState(true);
|
||||
sendRpcCall("spellcheck", getTextMatcher().text, markErrors, errorCallback);
|
||||
editor.focus();
|
||||
}
|
||||
|
||||
function checkIfFinished() {
|
||||
if (!editor.dom.select('span.mce-spellchecker-word').length) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
function addToDictionary(word, spans) {
|
||||
editor.setProgressState(true);
|
||||
|
||||
sendRpcCall("addToDictionary", word, function() {
|
||||
editor.setProgressState(false);
|
||||
editor.dom.remove(spans, true);
|
||||
checkIfFinished();
|
||||
}, function(message) {
|
||||
editor.windowManager.alert(message);
|
||||
editor.setProgressState(false);
|
||||
});
|
||||
}
|
||||
|
||||
function ignoreWord(word, spans, all) {
|
||||
editor.selection.collapse();
|
||||
|
||||
if (all) {
|
||||
Tools.each(editor.dom.select('span.mce-spellchecker-word'), function(span) {
|
||||
if (span.getAttribute('data-mce-word') == word) {
|
||||
editor.dom.remove(span, true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
editor.dom.remove(spans, true);
|
||||
}
|
||||
|
||||
checkIfFinished();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
getTextMatcher().reset();
|
||||
self.textMatcher = null;
|
||||
|
||||
if (started) {
|
||||
started = false;
|
||||
editor.fire('SpellcheckEnd');
|
||||
}
|
||||
}
|
||||
|
||||
function getElmIndex(elm) {
|
||||
var value = elm.getAttribute('data-mce-index');
|
||||
|
||||
if (typeof(value) == "number") {
|
||||
return "" + value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function findSpansByIndex(index) {
|
||||
var nodes, spans = [];
|
||||
|
||||
nodes = Tools.toArray(editor.getBody().getElementsByTagName('span'));
|
||||
if (nodes.length) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var nodeIndex = getElmIndex(nodes[i]);
|
||||
|
||||
if (nodeIndex === null || !nodeIndex.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nodeIndex === index.toString()) {
|
||||
spans.push(nodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spans;
|
||||
}
|
||||
|
||||
editor.on('click', function(e) {
|
||||
var target = e.target;
|
||||
|
||||
if (target.className == "mce-spellchecker-word") {
|
||||
e.preventDefault();
|
||||
|
||||
var spans = findSpansByIndex(getElmIndex(target));
|
||||
|
||||
if (spans.length > 0) {
|
||||
var rng = editor.dom.createRng();
|
||||
rng.setStartBefore(spans[0]);
|
||||
rng.setEndAfter(spans[spans.length - 1]);
|
||||
editor.selection.setRng(rng);
|
||||
showSuggestions(target.getAttribute('data-mce-word'), spans);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.addMenuItem('spellchecker', {
|
||||
text: 'Spellcheck',
|
||||
context: 'tools',
|
||||
onclick: spellcheck,
|
||||
selectable: true,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
self.active(started);
|
||||
|
||||
editor.on('SpellcheckStart SpellcheckEnd', function() {
|
||||
self.active(started);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function updateSelection(e) {
|
||||
var selectedLanguage = settings.spellchecker_language;
|
||||
|
||||
e.control.items().each(function(ctrl) {
|
||||
ctrl.active(ctrl.settings.data === selectedLanguage);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the specified words and marks them. It will also show suggestions for those words.
|
||||
*
|
||||
* @example
|
||||
* editor.plugins.spellchecker.markErrors({
|
||||
* dictionary: true,
|
||||
* words: {
|
||||
* "word1": ["suggestion 1", "Suggestion 2"]
|
||||
* }
|
||||
* });
|
||||
* @param {Object} data Data object containing the words with suggestions.
|
||||
*/
|
||||
function markErrors(data) {
|
||||
var suggestions;
|
||||
|
||||
if (data.words) {
|
||||
hasDictionarySupport = !!data.dictionary;
|
||||
suggestions = data.words;
|
||||
} else {
|
||||
// Fallback to old format
|
||||
suggestions = data;
|
||||
}
|
||||
|
||||
editor.setProgressState(false);
|
||||
|
||||
if (isEmpty(suggestions)) {
|
||||
editor.windowManager.alert('No misspellings found');
|
||||
started = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lastSuggestions = suggestions;
|
||||
|
||||
getTextMatcher().find(getWordCharPattern()).filter(function(match) {
|
||||
return !!suggestions[match.text];
|
||||
}).wrap(function(match) {
|
||||
return editor.dom.create('span', {
|
||||
"class": 'mce-spellchecker-word',
|
||||
"data-mce-bogus": 1,
|
||||
"data-mce-word": match.text
|
||||
});
|
||||
});
|
||||
|
||||
started = true;
|
||||
editor.fire('SpellcheckStart');
|
||||
}
|
||||
|
||||
var buttonArgs = {
|
||||
tooltip: 'Spellcheck',
|
||||
onclick: spellcheck,
|
||||
onPostRender: function() {
|
||||
var self = this;
|
||||
|
||||
editor.on('SpellcheckStart SpellcheckEnd', function() {
|
||||
self.active(started);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (languageMenuItems.length > 1) {
|
||||
buttonArgs.type = 'splitbutton';
|
||||
buttonArgs.menu = languageMenuItems;
|
||||
buttonArgs.onshow = updateSelection;
|
||||
buttonArgs.onselect = function(e) {
|
||||
settings.spellchecker_language = e.control.settings.data;
|
||||
};
|
||||
}
|
||||
|
||||
editor.addButton('spellchecker', buttonArgs);
|
||||
editor.addCommand('mceSpellCheck', spellcheck);
|
||||
|
||||
editor.on('remove', function() {
|
||||
if (suggestionsMenu) {
|
||||
suggestionsMenu.remove();
|
||||
suggestionsMenu = null;
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('change', checkIfFinished);
|
||||
|
||||
this.getTextMatcher = getTextMatcher;
|
||||
this.getWordCharPattern = getWordCharPattern;
|
||||
this.markErrors = markErrors;
|
||||
this.getLanguage = function() {
|
||||
return settings.spellchecker_language;
|
||||
};
|
||||
|
||||
// Set default spellchecker language if it's not specified
|
||||
settings.spellchecker_language = settings.spellchecker_language || settings.language || 'en';
|
||||
});
|
||||
});
|
||||
|
||||
expose(["tinymce/spellcheckerplugin/DomTextMatcher"]);
|
||||
})(this);
|
|
@ -0,0 +1,120 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('tabfocus', function(editor) {
|
||||
var DOM = tinymce.DOM, each = tinymce.each, explode = tinymce.explode;
|
||||
|
||||
function tabCancel(e) {
|
||||
if (e.keyCode === 9 && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function tabHandler(e) {
|
||||
var x, el, v, i;
|
||||
|
||||
if (e.keyCode !== 9 || e.ctrlKey || e.altKey || e.metaKey || e.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
function find(direction) {
|
||||
el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
|
||||
|
||||
function canSelectRecursive(e) {
|
||||
return e.nodeName === "BODY" || (e.type != 'hidden' &&
|
||||
e.style.display != "none" &&
|
||||
e.style.visibility != "hidden" && canSelectRecursive(e.parentNode));
|
||||
}
|
||||
|
||||
function canSelect(el) {
|
||||
return /INPUT|TEXTAREA|BUTTON/.test(el.tagName) && tinymce.get(e.id) && el.tabIndex != -1 && canSelectRecursive(el);
|
||||
}
|
||||
|
||||
each(el, function(e, i) {
|
||||
if (e.id == editor.id) {
|
||||
x = i;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (direction > 0) {
|
||||
for (i = x + 1; i < el.length; i++) {
|
||||
if (canSelect(el[i])) {
|
||||
return el[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = x - 1; i >= 0; i--) {
|
||||
if (canSelect(el[i])) {
|
||||
return el[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
v = explode(editor.getParam('tab_focus', editor.getParam('tabfocus_elements', ':prev,:next')));
|
||||
|
||||
if (v.length == 1) {
|
||||
v[1] = v[0];
|
||||
v[0] = ':prev';
|
||||
}
|
||||
|
||||
// Find element to focus
|
||||
if (e.shiftKey) {
|
||||
if (v[0] == ':prev') {
|
||||
el = find(-1);
|
||||
} else {
|
||||
el = DOM.get(v[0]);
|
||||
}
|
||||
} else {
|
||||
if (v[1] == ':next') {
|
||||
el = find(1);
|
||||
} else {
|
||||
el = DOM.get(v[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
var focusEditor = tinymce.get(el.id || el.name);
|
||||
|
||||
if (el.id && focusEditor) {
|
||||
focusEditor.focus();
|
||||
} else {
|
||||
window.setTimeout(function() {
|
||||
if (!tinymce.Env.webkit) {
|
||||
window.focus();
|
||||
}
|
||||
|
||||
el.focus();
|
||||
}, 10);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
editor.on('init', function() {
|
||||
if (editor.inline) {
|
||||
// Remove default tabIndex in inline mode
|
||||
tinymce.DOM.setAttrib(editor.getBody(), 'tabIndex', null);
|
||||
}
|
||||
|
||||
editor.on('keyup', tabCancel);
|
||||
|
||||
if (tinymce.Env.gecko) {
|
||||
editor.on('keypress keydown', tabHandler);
|
||||
} else {
|
||||
editor.on('keydown', tabHandler);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("tabfocus",function(a){function b(a){9!==a.keyCode||a.ctrlKey||a.altKey||a.metaKey||a.preventDefault()}function c(b){function c(c){function f(a){return"BODY"===a.nodeName||"hidden"!=a.type&&"none"!=a.style.display&&"hidden"!=a.style.visibility&&f(a.parentNode)}function i(a){return/INPUT|TEXTAREA|BUTTON/.test(a.tagName)&&tinymce.get(b.id)&&-1!=a.tabIndex&&f(a)}if(h=d.select(":input:enabled,*[tabindex]:not(iframe)"),e(h,function(b,c){return b.id==a.id?(g=c,!1):void 0}),c>0){for(j=g+1;j<h.length;j++)if(i(h[j]))return h[j]}else for(j=g-1;j>=0;j--)if(i(h[j]))return h[j];return null}var g,h,i,j;if(!(9!==b.keyCode||b.ctrlKey||b.altKey||b.metaKey||b.isDefaultPrevented())&&(i=f(a.getParam("tab_focus",a.getParam("tabfocus_elements",":prev,:next"))),1==i.length&&(i[1]=i[0],i[0]=":prev"),h=b.shiftKey?":prev"==i[0]?c(-1):d.get(i[0]):":next"==i[1]?c(1):d.get(i[1]))){var k=tinymce.get(h.id||h.name);h.id&&k?k.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),h.focus()},10),b.preventDefault()}}var d=tinymce.DOM,e=tinymce.each,f=tinymce.explode;a.on("init",function(){a.inline&&tinymce.DOM.setAttrib(a.getBody(),"tabIndex",null),a.on("keyup",b),tinymce.Env.gecko?a.on("keypress keydown",c):a.on("keydown",c)})});
|
|
@ -0,0 +1,262 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('template', function(editor) {
|
||||
var each = tinymce.each;
|
||||
|
||||
function createTemplateList(callback) {
|
||||
return function() {
|
||||
var templateList = editor.settings.templates;
|
||||
|
||||
if (typeof(templateList) == "string") {
|
||||
tinymce.util.XHR.send({
|
||||
url: templateList,
|
||||
success: function(text) {
|
||||
callback(tinymce.util.JSON.parse(text));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(templateList);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function showDialog(templateList) {
|
||||
var win, values = [], templateHtml;
|
||||
|
||||
if (!templateList || templateList.length === 0) {
|
||||
editor.windowManager.alert('No templates defined');
|
||||
return;
|
||||
}
|
||||
|
||||
tinymce.each(templateList, function(template) {
|
||||
values.push({
|
||||
selected: !values.length,
|
||||
text: template.title,
|
||||
value: {
|
||||
url: template.url,
|
||||
content: template.content,
|
||||
description: template.description
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function onSelectTemplate(e) {
|
||||
var value = e.control.value();
|
||||
|
||||
function insertIframeHtml(html) {
|
||||
if (html.indexOf('<html>') == -1) {
|
||||
var contentCssLinks = '';
|
||||
|
||||
tinymce.each(editor.contentCSS, function(url) {
|
||||
contentCssLinks += '<link type="text/css" rel="stylesheet" href="' + editor.documentBaseURI.toAbsolute(url) + '">';
|
||||
});
|
||||
|
||||
html = (
|
||||
'<!DOCTYPE html>' +
|
||||
'<html>' +
|
||||
'<head>' +
|
||||
contentCssLinks +
|
||||
'</head>' +
|
||||
'<body>' +
|
||||
html +
|
||||
'</body>' +
|
||||
'</html>'
|
||||
);
|
||||
}
|
||||
|
||||
html = replaceTemplateValues(html, 'template_preview_replace_values');
|
||||
|
||||
var doc = win.find('iframe')[0].getEl().contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(html);
|
||||
doc.close();
|
||||
}
|
||||
|
||||
if (value.url) {
|
||||
tinymce.util.XHR.send({
|
||||
url: value.url,
|
||||
success: function(html) {
|
||||
templateHtml = html;
|
||||
insertIframeHtml(templateHtml);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
templateHtml = value.content;
|
||||
insertIframeHtml(templateHtml);
|
||||
}
|
||||
|
||||
win.find('#description')[0].text(e.control.value().description);
|
||||
}
|
||||
|
||||
win = editor.windowManager.open({
|
||||
title: 'Insert template',
|
||||
layout: 'flex',
|
||||
direction: 'column',
|
||||
align: 'stretch',
|
||||
padding: 15,
|
||||
spacing: 10,
|
||||
|
||||
items: [
|
||||
{type: 'form', flex: 0, padding: 0, items: [
|
||||
{type: 'container', label: 'Templates', items: {
|
||||
type: 'listbox', label: 'Templates', name: 'template', values: values, onselect: onSelectTemplate
|
||||
}}
|
||||
]},
|
||||
{type: 'label', name: 'description', label: 'Description', text: '\u00a0'},
|
||||
{type: 'iframe', flex: 1, border: 1}
|
||||
],
|
||||
|
||||
onsubmit: function() {
|
||||
insertTemplate(false, templateHtml);
|
||||
},
|
||||
|
||||
width: editor.getParam('template_popup_width', 600),
|
||||
height: editor.getParam('template_popup_height', 500)
|
||||
});
|
||||
|
||||
win.find('listbox')[0].fire('select');
|
||||
}
|
||||
|
||||
function getDateTime(fmt, date) {
|
||||
var daysShort = "Sun Mon Tue Wed Thu Fri Sat Sun".split(' ');
|
||||
var daysLong = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(' ');
|
||||
var monthsShort = "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(' ');
|
||||
var monthsLong = "January February March April May June July August September October November December".split(' ');
|
||||
|
||||
function addZeros(value, len) {
|
||||
value = "" + value;
|
||||
|
||||
if (value.length < len) {
|
||||
for (var i = 0; i < (len - value.length); i++) {
|
||||
value = "0" + value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
date = date || new Date();
|
||||
|
||||
fmt = fmt.replace("%D", "%m/%d/%Y");
|
||||
fmt = fmt.replace("%r", "%I:%M:%S %p");
|
||||
fmt = fmt.replace("%Y", "" + date.getFullYear());
|
||||
fmt = fmt.replace("%y", "" + date.getYear());
|
||||
fmt = fmt.replace("%m", addZeros(date.getMonth() + 1, 2));
|
||||
fmt = fmt.replace("%d", addZeros(date.getDate(), 2));
|
||||
fmt = fmt.replace("%H", "" + addZeros(date.getHours(), 2));
|
||||
fmt = fmt.replace("%M", "" + addZeros(date.getMinutes(), 2));
|
||||
fmt = fmt.replace("%S", "" + addZeros(date.getSeconds(), 2));
|
||||
fmt = fmt.replace("%I", "" + ((date.getHours() + 11) % 12 + 1));
|
||||
fmt = fmt.replace("%p", "" + (date.getHours() < 12 ? "AM" : "PM"));
|
||||
fmt = fmt.replace("%B", "" + editor.translate(monthsLong[date.getMonth()]));
|
||||
fmt = fmt.replace("%b", "" + editor.translate(monthsShort[date.getMonth()]));
|
||||
fmt = fmt.replace("%A", "" + editor.translate(daysLong[date.getDay()]));
|
||||
fmt = fmt.replace("%a", "" + editor.translate(daysShort[date.getDay()]));
|
||||
fmt = fmt.replace("%%", "%");
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
function replaceVals(e) {
|
||||
var dom = editor.dom, vl = editor.getParam('template_replace_values');
|
||||
|
||||
each(dom.select('*', e), function(e) {
|
||||
each(vl, function(v, k) {
|
||||
if (dom.hasClass(e, k)) {
|
||||
if (typeof(vl[k]) == 'function') {
|
||||
vl[k](e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function replaceTemplateValues(html, templateValuesOptionName) {
|
||||
each(editor.getParam(templateValuesOptionName), function(v, k) {
|
||||
if (typeof(v) != 'function') {
|
||||
html = html.replace(new RegExp('\\{\\$' + k + '\\}', 'g'), v);
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function insertTemplate(ui, html) {
|
||||
var el, n, dom = editor.dom, sel = editor.selection.getContent();
|
||||
|
||||
html = replaceTemplateValues(html, 'template_replace_values');
|
||||
el = dom.create('div', null, html);
|
||||
|
||||
// Find template element within div
|
||||
n = dom.select('.mceTmpl', el);
|
||||
if (n && n.length > 0) {
|
||||
el = dom.create('div', null);
|
||||
el.appendChild(n[0].cloneNode(true));
|
||||
}
|
||||
|
||||
function hasClass(n, c) {
|
||||
return new RegExp('\\b' + c + '\\b', 'g').test(n.className);
|
||||
}
|
||||
|
||||
each(dom.select('*', el), function(n) {
|
||||
// Replace cdate
|
||||
if (hasClass(n, editor.getParam('template_cdate_classes', 'cdate').replace(/\s+/g, '|'))) {
|
||||
n.innerHTML = getDateTime(editor.getParam("template_cdate_format", editor.getLang("template.cdate_format")));
|
||||
}
|
||||
|
||||
// Replace mdate
|
||||
if (hasClass(n, editor.getParam('template_mdate_classes', 'mdate').replace(/\s+/g, '|'))) {
|
||||
n.innerHTML = getDateTime(editor.getParam("template_mdate_format", editor.getLang("template.mdate_format")));
|
||||
}
|
||||
|
||||
// Replace selection
|
||||
if (hasClass(n, editor.getParam('template_selected_content_classes', 'selcontent').replace(/\s+/g, '|'))) {
|
||||
n.innerHTML = sel;
|
||||
}
|
||||
});
|
||||
|
||||
replaceVals(el);
|
||||
|
||||
editor.execCommand('mceInsertContent', false, el.innerHTML);
|
||||
editor.addVisual();
|
||||
}
|
||||
|
||||
editor.addCommand('mceInsertTemplate', insertTemplate);
|
||||
|
||||
editor.addButton('template', {
|
||||
title: 'Insert template',
|
||||
onclick: createTemplateList(showDialog)
|
||||
});
|
||||
|
||||
editor.addMenuItem('template', {
|
||||
text: 'Insert template',
|
||||
onclick: createTemplateList(showDialog),
|
||||
context: 'insert'
|
||||
});
|
||||
|
||||
editor.on('PreProcess', function(o) {
|
||||
var dom = editor.dom;
|
||||
|
||||
each(dom.select('div', o.node), function(e) {
|
||||
if (dom.hasClass(e, 'mceTmpl')) {
|
||||
each(dom.select('*', e), function(e) {
|
||||
if (dom.hasClass(e, editor.getParam('template_mdate_classes', 'mdate').replace(/\s+/g, '|'))) {
|
||||
e.innerHTML = getDateTime(editor.getParam("template_mdate_format", editor.getLang("template.mdate_format")));
|
||||
}
|
||||
});
|
||||
|
||||
replaceVals(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("template",function(a){function b(b){return function(){var c=a.settings.templates;"string"==typeof c?tinymce.util.XHR.send({url:c,success:function(a){b(tinymce.util.JSON.parse(a))}}):b(c)}}function c(b){function c(b){function c(b){if(-1==b.indexOf("<html>")){var c="";tinymce.each(a.contentCSS,function(b){c+='<link type="text/css" rel="stylesheet" href="'+a.documentBaseURI.toAbsolute(b)+'">'}),b="<!DOCTYPE html><html><head>"+c+"</head><body>"+b+"</body></html>"}b=f(b,"template_preview_replace_values");var e=d.find("iframe")[0].getEl().contentWindow.document;e.open(),e.write(b),e.close()}var g=b.control.value();g.url?tinymce.util.XHR.send({url:g.url,success:function(a){e=a,c(e)}}):(e=g.content,c(e)),d.find("#description")[0].text(b.control.value().description)}var d,e,h=[];return b&&0!==b.length?(tinymce.each(b,function(a){h.push({selected:!h.length,text:a.title,value:{url:a.url,content:a.content,description:a.description}})}),d=a.windowManager.open({title:"Insert template",layout:"flex",direction:"column",align:"stretch",padding:15,spacing:10,items:[{type:"form",flex:0,padding:0,items:[{type:"container",label:"Templates",items:{type:"listbox",label:"Templates",name:"template",values:h,onselect:c}}]},{type:"label",name:"description",label:"Description",text:"\xa0"},{type:"iframe",flex:1,border:1}],onsubmit:function(){g(!1,e)},width:a.getParam("template_popup_width",600),height:a.getParam("template_popup_height",500)}),void d.find("listbox")[0].fire("select")):void a.windowManager.alert("No templates defined")}function d(b,c){function d(a,b){if(a=""+a,a.length<b)for(var c=0;c<b-a.length;c++)a="0"+a;return a}var e="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),f="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),g="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),h="January February March April May June July August September October November December".split(" ");return c=c||new Date,b=b.replace("%D","%m/%d/%Y"),b=b.replace("%r","%I:%M:%S %p"),b=b.replace("%Y",""+c.getFullYear()),b=b.replace("%y",""+c.getYear()),b=b.replace("%m",d(c.getMonth()+1,2)),b=b.replace("%d",d(c.getDate(),2)),b=b.replace("%H",""+d(c.getHours(),2)),b=b.replace("%M",""+d(c.getMinutes(),2)),b=b.replace("%S",""+d(c.getSeconds(),2)),b=b.replace("%I",""+((c.getHours()+11)%12+1)),b=b.replace("%p",""+(c.getHours()<12?"AM":"PM")),b=b.replace("%B",""+a.translate(h[c.getMonth()])),b=b.replace("%b",""+a.translate(g[c.getMonth()])),b=b.replace("%A",""+a.translate(f[c.getDay()])),b=b.replace("%a",""+a.translate(e[c.getDay()])),b=b.replace("%%","%")}function e(b){var c=a.dom,d=a.getParam("template_replace_values");h(c.select("*",b),function(a){h(d,function(b,e){c.hasClass(a,e)&&"function"==typeof d[e]&&d[e](a)})})}function f(b,c){return h(a.getParam(c),function(a,c){"function"!=typeof a&&(b=b.replace(new RegExp("\\{\\$"+c+"\\}","g"),a))}),b}function g(b,c){function g(a,b){return new RegExp("\\b"+b+"\\b","g").test(a.className)}var i,j,k=a.dom,l=a.selection.getContent();c=f(c,"template_replace_values"),i=k.create("div",null,c),j=k.select(".mceTmpl",i),j&&j.length>0&&(i=k.create("div",null),i.appendChild(j[0].cloneNode(!0))),h(k.select("*",i),function(b){g(b,a.getParam("template_cdate_classes","cdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_cdate_format",a.getLang("template.cdate_format")))),g(b,a.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_mdate_format",a.getLang("template.mdate_format")))),g(b,a.getParam("template_selected_content_classes","selcontent").replace(/\s+/g,"|"))&&(b.innerHTML=l)}),e(i),a.execCommand("mceInsertContent",!1,i.innerHTML),a.addVisual()}var h=tinymce.each;a.addCommand("mceInsertTemplate",g),a.addButton("template",{title:"Insert template",onclick:b(c)}),a.addMenuItem("template",{text:"Insert template",onclick:b(c),context:"insert"}),a.on("PreProcess",function(b){var c=a.dom;h(c.select("div",b.node),function(b){c.hasClass(b,"mceTmpl")&&(h(c.select("*",b),function(b){c.hasClass(b,a.getParam("template_mdate_classes","mdate").replace(/\s+/g,"|"))&&(b.innerHTML=d(a.getParam("template_mdate_format",a.getLang("template.mdate_format"))))}),e(b))})})});
|
|
@ -0,0 +1,272 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
/*eslint consistent-this:0 */
|
||||
|
||||
tinymce.PluginManager.add('textcolor', function(editor) {
|
||||
var cols, rows;
|
||||
|
||||
rows = editor.settings.textcolor_rows || 5;
|
||||
cols = editor.settings.textcolor_cols || 8;
|
||||
|
||||
function getCurrentColor(format) {
|
||||
var color;
|
||||
|
||||
editor.dom.getParents(editor.selection.getStart(), function(elm) {
|
||||
var value;
|
||||
|
||||
if ((value = elm.style[format == 'forecolor' ? 'color' : 'background-color'])) {
|
||||
color = value;
|
||||
}
|
||||
});
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
function mapColors() {
|
||||
var i, colors = [], colorMap;
|
||||
|
||||
colorMap = editor.settings.textcolor_map || [
|
||||
"000000", "Black",
|
||||
"993300", "Burnt orange",
|
||||
"333300", "Dark olive",
|
||||
"003300", "Dark green",
|
||||
"003366", "Dark azure",
|
||||
"000080", "Navy Blue",
|
||||
"333399", "Indigo",
|
||||
"333333", "Very dark gray",
|
||||
"800000", "Maroon",
|
||||
"FF6600", "Orange",
|
||||
"808000", "Olive",
|
||||
"008000", "Green",
|
||||
"008080", "Teal",
|
||||
"0000FF", "Blue",
|
||||
"666699", "Grayish blue",
|
||||
"808080", "Gray",
|
||||
"FF0000", "Red",
|
||||
"FF9900", "Amber",
|
||||
"99CC00", "Yellow green",
|
||||
"339966", "Sea green",
|
||||
"33CCCC", "Turquoise",
|
||||
"3366FF", "Royal blue",
|
||||
"800080", "Purple",
|
||||
"999999", "Medium gray",
|
||||
"FF00FF", "Magenta",
|
||||
"FFCC00", "Gold",
|
||||
"FFFF00", "Yellow",
|
||||
"00FF00", "Lime",
|
||||
"00FFFF", "Aqua",
|
||||
"00CCFF", "Sky blue",
|
||||
"993366", "Red violet",
|
||||
"FFFFFF", "White",
|
||||
"FF99CC", "Pink",
|
||||
"FFCC99", "Peach",
|
||||
"FFFF99", "Light yellow",
|
||||
"CCFFCC", "Pale green",
|
||||
"CCFFFF", "Pale cyan",
|
||||
"99CCFF", "Light sky blue",
|
||||
"CC99FF", "Plum"
|
||||
];
|
||||
|
||||
for (i = 0; i < colorMap.length; i += 2) {
|
||||
colors.push({
|
||||
text: colorMap[i + 1],
|
||||
color: '#' + colorMap[i]
|
||||
});
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
function renderColorPicker() {
|
||||
var ctrl = this, colors, color, html, last, x, y, i, id = ctrl._id, count = 0;
|
||||
|
||||
function getColorCellHtml(color, title) {
|
||||
var isNoColor = color == 'transparent';
|
||||
|
||||
return (
|
||||
'<td class="mce-grid-cell' + (isNoColor ? ' mce-colorbtn-trans' : '') + '">' +
|
||||
'<div id="' + id + '-' + (count++) + '"' +
|
||||
' data-mce-color="' + (color ? color : '') + '"' +
|
||||
' role="option"' +
|
||||
' tabIndex="-1"' +
|
||||
' style="' + (color ? 'background-color: ' + color : '') + '"' +
|
||||
' title="' + tinymce.translate(title) + '">' +
|
||||
(isNoColor ? '×' : '') +
|
||||
'</div>' +
|
||||
'</td>'
|
||||
);
|
||||
}
|
||||
|
||||
colors = mapColors();
|
||||
colors.push({
|
||||
text: tinymce.translate("No color"),
|
||||
color: "transparent"
|
||||
});
|
||||
|
||||
html = '<table class="mce-grid mce-grid-border mce-colorbutton-grid" role="list" cellspacing="0"><tbody>';
|
||||
last = colors.length - 1;
|
||||
|
||||
for (y = 0; y < rows; y++) {
|
||||
html += '<tr>';
|
||||
|
||||
for (x = 0; x < cols; x++) {
|
||||
i = y * cols + x;
|
||||
|
||||
if (i > last) {
|
||||
html += '<td></td>';
|
||||
} else {
|
||||
color = colors[i];
|
||||
html += getColorCellHtml(color.color, color.text);
|
||||
}
|
||||
}
|
||||
|
||||
html += '</tr>';
|
||||
}
|
||||
|
||||
if (editor.settings.color_picker_callback) {
|
||||
html += (
|
||||
'<tr>' +
|
||||
'<td colspan="' + cols + '" class="mce-custom-color-btn">' +
|
||||
'<div id="' + id + '-c" class="mce-widget mce-btn mce-btn-small mce-btn-flat" ' +
|
||||
'role="button" tabindex="-1" aria-labelledby="' + id + '-c" style="width: 100%">' +
|
||||
'<button type="button" role="presentation" tabindex="-1">' + tinymce.translate('Custom...') + '</button>' +
|
||||
'</div>' +
|
||||
'</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
|
||||
html += '<tr>';
|
||||
|
||||
for (x = 0; x < cols; x++) {
|
||||
html += getColorCellHtml('', 'Custom color');
|
||||
}
|
||||
|
||||
html += '</tr>';
|
||||
}
|
||||
|
||||
html += '</tbody></table>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function applyFormat(format, value) {
|
||||
editor.focus();
|
||||
editor.formatter.apply(format, {value: value});
|
||||
editor.nodeChanged();
|
||||
}
|
||||
|
||||
function removeFormat(format) {
|
||||
editor.focus();
|
||||
editor.formatter.remove(format, {value: null}, null, true);
|
||||
editor.nodeChanged();
|
||||
}
|
||||
|
||||
function onPanelClick(e) {
|
||||
var buttonCtrl = this.parent(), value;
|
||||
|
||||
function selectColor(value) {
|
||||
buttonCtrl.hidePanel();
|
||||
buttonCtrl.color(value);
|
||||
applyFormat(buttonCtrl.settings.format, value);
|
||||
}
|
||||
|
||||
function setDivColor(div, value) {
|
||||
div.style.background = value;
|
||||
div.setAttribute('data-mce-color', value);
|
||||
}
|
||||
|
||||
if (tinymce.DOM.getParent(e.target, '.mce-custom-color-btn')) {
|
||||
buttonCtrl.hidePanel();
|
||||
|
||||
editor.settings.color_picker_callback.call(editor, function(value) {
|
||||
var tableElm = buttonCtrl.panel.getEl().getElementsByTagName('table')[0];
|
||||
var customColorCells, div, i;
|
||||
|
||||
customColorCells = tinymce.map(tableElm.rows[tableElm.rows.length - 1].childNodes, function(elm) {
|
||||
return elm.firstChild;
|
||||
});
|
||||
|
||||
for (i = 0; i < customColorCells.length; i++) {
|
||||
div = customColorCells[i];
|
||||
if (!div.getAttribute('data-mce-color')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift colors to the right
|
||||
// TODO: Might need to be the left on RTL
|
||||
if (i == cols) {
|
||||
for (i = 0; i < cols - 1; i++) {
|
||||
setDivColor(customColorCells[i], customColorCells[i + 1].getAttribute('data-mce-color'));
|
||||
}
|
||||
}
|
||||
|
||||
setDivColor(div, value);
|
||||
selectColor(value);
|
||||
}, getCurrentColor(buttonCtrl.settings.format));
|
||||
}
|
||||
|
||||
value = e.target.getAttribute('data-mce-color');
|
||||
if (value) {
|
||||
if (this.lastId) {
|
||||
document.getElementById(this.lastId).setAttribute('aria-selected', false);
|
||||
}
|
||||
|
||||
e.target.setAttribute('aria-selected', true);
|
||||
this.lastId = e.target.id;
|
||||
|
||||
if (value == 'transparent') {
|
||||
removeFormat(buttonCtrl.settings.format);
|
||||
buttonCtrl.hidePanel();
|
||||
return;
|
||||
}
|
||||
|
||||
selectColor(value);
|
||||
} else if (value !== null) {
|
||||
buttonCtrl.hidePanel();
|
||||
}
|
||||
}
|
||||
|
||||
function onButtonClick() {
|
||||
var self = this;
|
||||
|
||||
if (self._color) {
|
||||
applyFormat(self.settings.format, self._color);
|
||||
}
|
||||
}
|
||||
|
||||
editor.addButton('forecolor', {
|
||||
type: 'colorbutton',
|
||||
tooltip: 'Text color',
|
||||
format: 'forecolor',
|
||||
panel: {
|
||||
role: 'application',
|
||||
ariaRemember: true,
|
||||
html: renderColorPicker,
|
||||
onclick: onPanelClick
|
||||
},
|
||||
onclick: onButtonClick
|
||||
});
|
||||
|
||||
editor.addButton('backcolor', {
|
||||
type: 'colorbutton',
|
||||
tooltip: 'Background color',
|
||||
format: 'hilitecolor',
|
||||
panel: {
|
||||
role: 'application',
|
||||
ariaRemember: true,
|
||||
html: renderColorPicker,
|
||||
onclick: onPanelClick
|
||||
},
|
||||
onclick: onButtonClick
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("textcolor",function(a){function b(b){var c;return a.dom.getParents(a.selection.getStart(),function(a){var d;(d=a.style["forecolor"==b?"color":"background-color"])&&(c=d)}),c}function c(){var b,c,d=[];for(c=a.settings.textcolor_map||["000000","Black","993300","Burnt orange","333300","Dark olive","003300","Dark green","003366","Dark azure","000080","Navy Blue","333399","Indigo","333333","Very dark gray","800000","Maroon","FF6600","Orange","808000","Olive","008000","Green","008080","Teal","0000FF","Blue","666699","Grayish blue","808080","Gray","FF0000","Red","FF9900","Amber","99CC00","Yellow green","339966","Sea green","33CCCC","Turquoise","3366FF","Royal blue","800080","Purple","999999","Medium gray","FF00FF","Magenta","FFCC00","Gold","FFFF00","Yellow","00FF00","Lime","00FFFF","Aqua","00CCFF","Sky blue","993366","Red violet","FFFFFF","White","FF99CC","Pink","FFCC99","Peach","FFFF99","Light yellow","CCFFCC","Pale green","CCFFFF","Pale cyan","99CCFF","Light sky blue","CC99FF","Plum"],b=0;b<c.length;b+=2)d.push({text:c[b+1],color:"#"+c[b]});return d}function d(){function b(a,b){var c="transparent"==a;return'<td class="mce-grid-cell'+(c?" mce-colorbtn-trans":"")+'"><div id="'+n+"-"+o++ +'" data-mce-color="'+(a?a:"")+'" role="option" tabIndex="-1" style="'+(a?"background-color: "+a:"")+'" title="'+tinymce.translate(b)+'">'+(c?"×":"")+"</div></td>"}var d,e,f,g,h,k,l,m=this,n=m._id,o=0;for(d=c(),d.push({text:tinymce.translate("No color"),color:"transparent"}),f='<table class="mce-grid mce-grid-border mce-colorbutton-grid" role="list" cellspacing="0"><tbody>',g=d.length-1,k=0;j>k;k++){for(f+="<tr>",h=0;i>h;h++)l=k*i+h,l>g?f+="<td></td>":(e=d[l],f+=b(e.color,e.text));f+="</tr>"}if(a.settings.color_picker_callback){for(f+='<tr><td colspan="'+i+'" class="mce-custom-color-btn"><div id="'+n+'-c" class="mce-widget mce-btn mce-btn-small mce-btn-flat" role="button" tabindex="-1" aria-labelledby="'+n+'-c" style="width: 100%"><button type="button" role="presentation" tabindex="-1">'+tinymce.translate("Custom...")+"</button></div></td></tr>",f+="<tr>",h=0;i>h;h++)f+=b("","Custom color");f+="</tr>"}return f+="</tbody></table>"}function e(b,c){a.focus(),a.formatter.apply(b,{value:c}),a.nodeChanged()}function f(b){a.focus(),a.formatter.remove(b,{value:null},null,!0),a.nodeChanged()}function g(c){function d(a){j.hidePanel(),j.color(a),e(j.settings.format,a)}function g(a,b){a.style.background=b,a.setAttribute("data-mce-color",b)}var h,j=this.parent();if(tinymce.DOM.getParent(c.target,".mce-custom-color-btn")&&(j.hidePanel(),a.settings.color_picker_callback.call(a,function(a){var b,c,e,f=j.panel.getEl().getElementsByTagName("table")[0];for(b=tinymce.map(f.rows[f.rows.length-1].childNodes,function(a){return a.firstChild}),e=0;e<b.length&&(c=b[e],c.getAttribute("data-mce-color"));e++);if(e==i)for(e=0;i-1>e;e++)g(b[e],b[e+1].getAttribute("data-mce-color"));g(c,a),d(a)},b(j.settings.format))),h=c.target.getAttribute("data-mce-color")){if(this.lastId&&document.getElementById(this.lastId).setAttribute("aria-selected",!1),c.target.setAttribute("aria-selected",!0),this.lastId=c.target.id,"transparent"==h)return f(j.settings.format),void j.hidePanel();d(h)}else null!==h&&j.hidePanel()}function h(){var a=this;a._color&&e(a.settings.format,a._color)}var i,j;j=a.settings.textcolor_rows||5,i=a.settings.textcolor_cols||8,a.addButton("forecolor",{type:"colorbutton",tooltip:"Text color",format:"forecolor",panel:{role:"application",ariaRemember:!0,html:d,onclick:g},onclick:h}),a.addButton("backcolor",{type:"colorbutton",tooltip:"Background color",format:"hilitecolor",panel:{role:"application",ariaRemember:!0,html:d,onclick:g},onclick:h})});
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('textpattern', function(editor) {
|
||||
var isPatternsDirty = true, patterns;
|
||||
|
||||
patterns = editor.settings.textpattern_patterns || [
|
||||
{start: '*', end: '*', format: 'italic'},
|
||||
{start: '**', end: '**', format: 'bold'},
|
||||
{start: '#', format: 'h1'},
|
||||
{start: '##', format: 'h2'},
|
||||
{start: '###', format: 'h3'},
|
||||
{start: '####', format: 'h4'},
|
||||
{start: '#####', format: 'h5'},
|
||||
{start: '######', format: 'h6'},
|
||||
{start: '1. ', cmd: 'InsertOrderedList'},
|
||||
{start: '* ', cmd: 'InsertUnorderedList'},
|
||||
{start: '- ', cmd: 'InsertUnorderedList'}
|
||||
];
|
||||
|
||||
// Returns a sorted patterns list, ordered descending by start length
|
||||
function getPatterns() {
|
||||
if (isPatternsDirty) {
|
||||
patterns.sort(function(a, b) {
|
||||
if (a.start.length > b.start.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.start.length < b.start.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
isPatternsDirty = false;
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
// Finds a matching pattern to the specified text
|
||||
function findPattern(text) {
|
||||
var patterns = getPatterns();
|
||||
|
||||
for (var i = 0; i < patterns.length; i++) {
|
||||
if (text.indexOf(patterns[i].start) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patterns[i].end && text.lastIndexOf(patterns[i].end) != text.length - patterns[i].end.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return patterns[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Finds the best matching end pattern
|
||||
function findEndPattern(text, offset, delta) {
|
||||
var patterns, pattern, i;
|
||||
|
||||
// Find best matching end
|
||||
patterns = getPatterns();
|
||||
for (i = 0; i < patterns.length; i++) {
|
||||
pattern = patterns[i];
|
||||
if (pattern.end && text.substr(offset - pattern.end.length - delta, pattern.end.length) == pattern.end) {
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles inline formats like *abc* and **abc**
|
||||
function applyInlineFormat(space) {
|
||||
var selection, dom, rng, container, offset, startOffset, text, patternRng, pattern, delta, format;
|
||||
|
||||
function splitContainer() {
|
||||
// Split text node and remove start/end from text node
|
||||
container = container.splitText(startOffset);
|
||||
container.splitText(offset - startOffset - delta);
|
||||
container.deleteData(0, pattern.start.length);
|
||||
container.deleteData(container.data.length - pattern.end.length, pattern.end.length);
|
||||
}
|
||||
|
||||
selection = editor.selection;
|
||||
dom = editor.dom;
|
||||
|
||||
if (!selection.isCollapsed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rng = selection.getRng(true);
|
||||
container = rng.startContainer;
|
||||
offset = rng.startOffset;
|
||||
text = container.data;
|
||||
delta = space ? 1 : 0;
|
||||
|
||||
if (container.nodeType != 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find best matching end
|
||||
pattern = findEndPattern(text, offset, delta);
|
||||
if (!pattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find start of matched pattern
|
||||
// TODO: Might need to improve this if there is nested formats
|
||||
startOffset = Math.max(0, offset - delta);
|
||||
startOffset = text.lastIndexOf(pattern.start, startOffset - pattern.end.length - 1);
|
||||
|
||||
if (startOffset === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup a range for the matching word
|
||||
patternRng = dom.createRng();
|
||||
patternRng.setStart(container, startOffset);
|
||||
patternRng.setEnd(container, offset - delta);
|
||||
pattern = findPattern(patternRng.toString());
|
||||
|
||||
if (!pattern || !pattern.end) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If container match doesn't have anything between start/end then do nothing
|
||||
if (container.data.length <= pattern.start.length + pattern.end.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
format = editor.formatter.get(pattern.format);
|
||||
if (format && format[0].inline) {
|
||||
splitContainer();
|
||||
editor.formatter.apply(pattern.format, {}, container);
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
// Handles block formats like ##abc or 1. abc
|
||||
function applyBlockFormat() {
|
||||
var selection, dom, container, firstTextNode, node, format, textBlockElm, pattern, walker, rng, offset;
|
||||
|
||||
selection = editor.selection;
|
||||
dom = editor.dom;
|
||||
|
||||
if (!selection.isCollapsed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
textBlockElm = dom.getParent(selection.getStart(), 'p');
|
||||
if (textBlockElm) {
|
||||
walker = new tinymce.dom.TreeWalker(textBlockElm, textBlockElm);
|
||||
while ((node = walker.next())) {
|
||||
if (node.nodeType == 3) {
|
||||
firstTextNode = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstTextNode) {
|
||||
pattern = findPattern(firstTextNode.data);
|
||||
if (!pattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
rng = selection.getRng(true);
|
||||
container = rng.startContainer;
|
||||
offset = rng.startOffset;
|
||||
|
||||
if (firstTextNode == container) {
|
||||
offset = Math.max(0, offset - pattern.start.length);
|
||||
}
|
||||
|
||||
if (tinymce.trim(firstTextNode.data).length == pattern.start.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pattern.format) {
|
||||
format = editor.formatter.get(pattern.format);
|
||||
if (format && format[0].block) {
|
||||
firstTextNode.deleteData(0, pattern.start.length);
|
||||
editor.formatter.apply(pattern.format, {}, firstTextNode);
|
||||
|
||||
rng.setStart(container, offset);
|
||||
rng.collapse(true);
|
||||
selection.setRng(rng);
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern.cmd) {
|
||||
editor.undoManager.transact(function() {
|
||||
firstTextNode.deleteData(0, pattern.start.length);
|
||||
editor.execCommand(pattern.cmd);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter() {
|
||||
var rng, wrappedTextNode;
|
||||
|
||||
wrappedTextNode = applyInlineFormat();
|
||||
if (wrappedTextNode) {
|
||||
rng = editor.dom.createRng();
|
||||
rng.setStart(wrappedTextNode, wrappedTextNode.data.length);
|
||||
rng.setEnd(wrappedTextNode, wrappedTextNode.data.length);
|
||||
editor.selection.setRng(rng);
|
||||
}
|
||||
|
||||
applyBlockFormat();
|
||||
}
|
||||
|
||||
function handleSpace() {
|
||||
var wrappedTextNode, lastChar, lastCharNode, rng, dom;
|
||||
|
||||
wrappedTextNode = applyInlineFormat(true);
|
||||
if (wrappedTextNode) {
|
||||
dom = editor.dom;
|
||||
lastChar = wrappedTextNode.data.slice(-1);
|
||||
|
||||
// Move space after the newly formatted node
|
||||
if (/[\u00a0 ]/.test(lastChar)) {
|
||||
wrappedTextNode.deleteData(wrappedTextNode.data.length - 1, 1);
|
||||
lastCharNode = dom.doc.createTextNode(lastChar);
|
||||
|
||||
if (wrappedTextNode.nextSibling) {
|
||||
dom.insertAfter(lastCharNode, wrappedTextNode.nextSibling);
|
||||
} else {
|
||||
wrappedTextNode.parentNode.appendChild(lastCharNode);
|
||||
}
|
||||
|
||||
rng = dom.createRng();
|
||||
rng.setStart(lastCharNode, 1);
|
||||
rng.setEnd(lastCharNode, 1);
|
||||
editor.selection.setRng(rng);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor.on('keydown', function(e) {
|
||||
if (e.keyCode == 13 && !tinymce.util.VK.modifierPressed(e)) {
|
||||
handleEnter();
|
||||
}
|
||||
}, true);
|
||||
|
||||
editor.on('keyup', function(e) {
|
||||
if (e.keyCode == 32 && !tinymce.util.VK.modifierPressed(e)) {
|
||||
handleSpace();
|
||||
}
|
||||
});
|
||||
|
||||
this.getPatterns = getPatterns;
|
||||
this.setPatterns = function(newPatterns) {
|
||||
patterns = newPatterns;
|
||||
isPatternsDirty = true;
|
||||
};
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
tinymce.PluginManager.add("textpattern",function(a){function b(){return j&&(i.sort(function(a,b){return a.start.length>b.start.length?-1:a.start.length<b.start.length?1:0}),j=!1),i}function c(a){for(var c=b(),d=0;d<c.length;d++)if(0===a.indexOf(c[d].start)&&(!c[d].end||a.lastIndexOf(c[d].end)==a.length-c[d].end.length))return c[d]}function d(a,c,d){var e,f,g;for(e=b(),g=0;g<e.length;g++)if(f=e[g],f.end&&a.substr(c-f.end.length-d,f.end.length)==f.end)return f}function e(b){function e(){i=i.splitText(k),i.splitText(j-k-o),i.deleteData(0,n.start.length),i.deleteData(i.data.length-n.end.length,n.end.length)}var f,g,h,i,j,k,l,m,n,o,p;return f=a.selection,g=a.dom,f.isCollapsed()&&(h=f.getRng(!0),i=h.startContainer,j=h.startOffset,l=i.data,o=b?1:0,3==i.nodeType&&(n=d(l,j,o),n&&(k=Math.max(0,j-o),k=l.lastIndexOf(n.start,k-n.end.length-1),-1!==k&&(m=g.createRng(),m.setStart(i,k),m.setEnd(i,j-o),n=c(m.toString()),n&&n.end&&!(i.data.length<=n.start.length+n.end.length)))))?(p=a.formatter.get(n.format),p&&p[0].inline?(e(),a.formatter.apply(n.format,{},i),i):void 0):void 0}function f(){var b,d,e,f,g,h,i,j,k,l,m;if(b=a.selection,d=a.dom,b.isCollapsed()&&(i=d.getParent(b.getStart(),"p"))){for(k=new tinymce.dom.TreeWalker(i,i);g=k.next();)if(3==g.nodeType){f=g;break}if(f){if(j=c(f.data),!j)return;if(l=b.getRng(!0),e=l.startContainer,m=l.startOffset,f==e&&(m=Math.max(0,m-j.start.length)),tinymce.trim(f.data).length==j.start.length)return;j.format&&(h=a.formatter.get(j.format),h&&h[0].block&&(f.deleteData(0,j.start.length),a.formatter.apply(j.format,{},f),l.setStart(e,m),l.collapse(!0),b.setRng(l))),j.cmd&&a.undoManager.transact(function(){f.deleteData(0,j.start.length),a.execCommand(j.cmd)})}}}function g(){var b,c;c=e(),c&&(b=a.dom.createRng(),b.setStart(c,c.data.length),b.setEnd(c,c.data.length),a.selection.setRng(b)),f()}function h(){var b,c,d,f,g;b=e(!0),b&&(g=a.dom,c=b.data.slice(-1),/[\u00a0 ]/.test(c)&&(b.deleteData(b.data.length-1,1),d=g.doc.createTextNode(c),b.nextSibling?g.insertAfter(d,b.nextSibling):b.parentNode.appendChild(d),f=g.createRng(),f.setStart(d,1),f.setEnd(d,1),a.selection.setRng(f)))}var i,j=!0;i=a.settings.textpattern_patterns||[{start:"*",end:"*",format:"italic"},{start:"**",end:"**",format:"bold"},{start:"#",format:"h1"},{start:"##",format:"h2"},{start:"###",format:"h3"},{start:"####",format:"h4"},{start:"#####",format:"h5"},{start:"######",format:"h6"},{start:"1. ",cmd:"InsertOrderedList"},{start:"* ",cmd:"InsertUnorderedList"},{start:"- ",cmd:"InsertUnorderedList"}],a.on("keydown",function(a){13!=a.keyCode||tinymce.util.VK.modifierPressed(a)||g()},!0),a.on("keyup",function(a){32!=a.keyCode||tinymce.util.VK.modifierPressed(a)||h()}),this.getPatterns=b,this.setPatterns=function(a){i=a,j=!0}});
|
|
@ -0,0 +1,135 @@
|
|||
.mce-visualblocks p {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks h1 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks h2 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks h3 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks h4 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks h5 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks h6 {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks div {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks section {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=);
|
||||
}
|
||||
|
||||
.mce-visualblocks article {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks blockquote {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks address {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=);
|
||||
}
|
||||
|
||||
.mce-visualblocks pre {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin-left: 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks figure {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks hgroup {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7);
|
||||
}
|
||||
|
||||
.mce-visualblocks aside {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=);
|
||||
}
|
||||
|
||||
.mce-visualblocks figcaption {
|
||||
border: 1px dashed #BBB;
|
||||
}
|
||||
|
||||
.mce-visualblocks ul {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)
|
||||
}
|
||||
|
||||
.mce-visualblocks ol {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==);
|
||||
}
|
||||
|
||||
.mce-visualblocks dl {
|
||||
padding-top: 10px;
|
||||
border: 1px dashed #BBB;
|
||||
margin: 0 0 1em 3px;
|
||||
background: transparent no-repeat url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* plugin.js
|
||||
*
|
||||
* Copyright 2012, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://www.tinymce.com/license
|
||||
* Contributing: http://www.tinymce.com/contributing
|
||||
*/
|
||||
|
||||
/*global tinymce:true */
|
||||
|
||||
tinymce.PluginManager.add('visualblocks', function(editor, url) {
|
||||
var cssId, visualBlocksMenuItem, enabled;
|
||||
|
||||
// We don't support older browsers like IE6/7 and they don't provide prototypes for DOM objects
|
||||
if (!window.NodeList) {
|
||||
return;
|
||||
}
|
||||
|
||||
function toggleActiveState() {
|
||||
var self = this;
|
||||
|
||||
self.active(enabled);
|
||||
|
||||
editor.on('VisualBlocks', function() {
|
||||
self.active(editor.dom.hasClass(editor.getBody(), 'mce-visualblocks'));
|
||||
});
|
||||
}
|
||||
|
||||
editor.addCommand('mceVisualBlocks', function() {
|
||||
var dom = editor.dom, linkElm;
|
||||
|
||||
if (!cssId) {
|
||||
cssId = dom.uniqueId();
|
||||
linkElm = dom.create('link', {
|
||||
id: cssId,
|
||||
rel: 'stylesheet',
|
||||
href: url + '/css/visualblocks.css'
|
||||
});
|
||||
|
||||
editor.getDoc().getElementsByTagName('head')[0].appendChild(linkElm);
|
||||
}
|
||||
|
||||
// Toggle on/off visual blocks while computing previews
|
||||
editor.on("PreviewFormats AfterPreviewFormats", function(e) {
|
||||
if (enabled) {
|
||||
dom.toggleClass(editor.getBody(), 'mce-visualblocks', e.type == "afterpreviewformats");
|
||||
}
|
||||
});
|
||||
|
||||
dom.toggleClass(editor.getBody(), 'mce-visualblocks');
|
||||
enabled = editor.dom.hasClass(editor.getBody(), 'mce-visualblocks');
|
||||
|
||||
if (visualBlocksMenuItem) {
|
||||
visualBlocksMenuItem.active(dom.hasClass(editor.getBody(), 'mce-visualblocks'));
|
||||
}
|
||||
|
||||
editor.fire('VisualBlocks');
|
||||
});
|
||||
|
||||
editor.addButton('visualblocks', {
|
||||
title: 'Show blocks',
|
||||
cmd: 'mceVisualBlocks',
|
||||
onPostRender: toggleActiveState
|
||||
});
|
||||
|
||||
editor.addMenuItem('visualblocks', {
|
||||
text: 'Show blocks',
|
||||
cmd: 'mceVisualBlocks',
|
||||
onPostRender: toggleActiveState,
|
||||
selectable: true,
|
||||
context: 'view',
|
||||
prependToContext: true
|
||||
});
|
||||
|
||||
editor.on('init', function() {
|
||||
if (editor.settings.visualblocks_default_state) {
|
||||
editor.execCommand('mceVisualBlocks', false, null, {skip_focus: true});
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('remove', function() {
|
||||
editor.dom.removeClass(editor.getBody(), 'mce-visualblocks');
|
||||
});
|
||||
});
|