st-rich 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +289 -0
- data/Rakefile +39 -0
- data/app/assets/images/rich/document-thumb.png +0 -0
- data/app/assets/images/rich/files.png +0 -0
- data/app/assets/images/rich/images.png +0 -0
- data/app/assets/images/rich/insert-many.png +0 -0
- data/app/assets/images/rich/insert-one.png +0 -0
- data/app/assets/images/rich/loading.png +0 -0
- data/app/assets/images/rich/menu-dash.png +0 -0
- data/app/assets/images/rich/plus-red.png +0 -0
- data/app/assets/images/rich/plus.png +0 -0
- data/app/assets/images/rich/rich.png +0 -0
- data/app/assets/images/rich/view-grid.png +0 -0
- data/app/assets/images/rich/view-list.png +0 -0
- data/app/assets/images/rich/x-red.png +0 -0
- data/app/assets/javascripts/rich/application.js +12 -0
- data/app/assets/javascripts/rich/base.js.erb +5 -0
- data/app/assets/javascripts/rich/browser/extensions.js +15 -0
- data/app/assets/javascripts/rich/browser/filebrowser.js +186 -0
- data/app/assets/javascripts/rich/browser/uploader.js +68 -0
- data/app/assets/javascripts/rich/editor/ckeditor_path.js.erb +1 -0
- data/app/assets/javascripts/rich/editor/rich_editor.js.erb +14 -0
- data/app/assets/javascripts/rich/editor/rich_picker.js +45 -0
- data/app/assets/stylesheets/rich/application.css.scss +365 -0
- data/app/assets/stylesheets/rich/editor.css +20 -0
- data/app/assets/stylesheets/rich/files.css +4 -0
- data/app/assets/stylesheets/rich/mixins/reset.css.scss +58 -0
- data/app/controllers/rich/files_controller.rb +82 -0
- data/app/helpers/rich/application_helper.rb +4 -0
- data/app/helpers/rich/files_helper.rb +13 -0
- data/app/inputs/rich_input.rb +19 -0
- data/app/inputs/rich_picker_input.rb +88 -0
- data/app/models/rich/rich_file.rb +75 -0
- data/app/views/layouts/rich/application.html.erb +14 -0
- data/app/views/rails_admin/main/_form_rich_picker.html.haml +7 -0
- data/app/views/rails_admin/main/_form_rich_text.html.haml +7 -0
- data/app/views/rich/files/_file.html.erb +11 -0
- data/app/views/rich/files/destroy.js.erb +1 -0
- data/app/views/rich/files/index.html.erb +38 -0
- data/app/views/rich/files/index.js.erb +1 -0
- data/app/views/rich/files/show.html.erb +1 -0
- data/config/locales/en.yml +19 -0
- data/config/locales/fr.yml +14 -0
- data/config/locales/it.yml +19 -0
- data/config/locales/nl.yml +14 -0
- data/config/locales/ru.yml +19 -0
- data/config/locales/sv.yml +14 -0
- data/config/locales/zh-CN.yml +19 -0
- data/config/routes.rb +5 -0
- data/db/migrate/20111002142937_create_rich_rich_images.rb +17 -0
- data/db/migrate/20111117202133_add_uri_cache_to_rich_image.rb +5 -0
- data/db/migrate/20111201095829_refactor_image_to_file.rb +12 -0
- data/lib/generators/rich/install/install_generator.rb +34 -0
- data/lib/generators/rich/install/templates/rich.js +1 -0
- data/lib/generators/rich/install/templates/rich.rb.erb +125 -0
- data/lib/rich.rb +195 -0
- data/lib/rich/authorize.rb +7 -0
- data/lib/rich/backends/carrierwave.rb +78 -0
- data/lib/rich/backends/paperclip.rb +79 -0
- data/lib/rich/backends/rich_file_uploader.rb +55 -0
- data/lib/rich/engine.rb +26 -0
- data/lib/rich/integrations/legacy_formtastic.rb +50 -0
- data/lib/rich/rails_admin/config/fields/types/rich_editor.rb +34 -0
- data/lib/rich/rails_admin/config/fields/types/rich_picker.rb +45 -0
- data/lib/rich/utils/file_size_validator.rb +68 -0
- data/lib/rich/version.rb +3 -0
- data/lib/tasks/rich_tasks.rake +24 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/admin/dashboards.rb +38 -0
- data/test/dummy/app/admin/posts.rb +11 -0
- data/test/dummy/app/assets/javascripts/active_admin.js +2 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/javascripts/posts.js +2 -0
- data/test/dummy/app/assets/javascripts/rich.js +1 -0
- data/test/dummy/app/assets/stylesheets/active_admin.css.scss +10 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/assets/stylesheets/posts.css +4 -0
- data/test/dummy/app/assets/stylesheets/rich/editor.css +20 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/posts_controller.rb +83 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/posts_helper.rb +2 -0
- data/test/dummy/app/models/admin_user.rb +9 -0
- data/test/dummy/app/models/post.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/posts/_form.html.erb +29 -0
- data/test/dummy/app/views/posts/_formtastic_form.html.erb +17 -0
- data/test/dummy/app/views/posts/edit.html.erb +6 -0
- data/test/dummy/app/views/posts/index.html.erb +27 -0
- data/test/dummy/app/views/posts/new.html.erb +5 -0
- data/test/dummy/app/views/posts/show.html.erb +20 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +48 -0
- data/test/dummy/config/boot.rb +19 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +32 -0
- data/test/dummy/config/environments/production.rb +62 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/active_admin.rb +101 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/devise.rb +211 -0
- data/test/dummy/config/initializers/formtastic.rb +80 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/rich.rb +29 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/devise.en.yml +58 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +13 -0
- data/test/dummy/db/migrate/20111001122048_create_posts.rb +12 -0
- data/test/dummy/db/migrate/20111003150957_create_rich_rich_images.rb +17 -0
- data/test/dummy/db/migrate/20111014123344_devise_create_admin_users.rb +31 -0
- data/test/dummy/db/migrate/20111014143348_create_admin_notes.rb +16 -0
- data/test/dummy/db/migrate/20111014143349_move_admin_notes_to_comments.rb +25 -0
- data/test/dummy/db/migrate/20111117202523_add_uri_cache_to_rich_image.rb +5 -0
- data/test/dummy/db/migrate/20111201102914_refactor_image_to_file.rb +12 -0
- data/test/dummy/db/schema.rb +58 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/fixtures/admin_users.yml +11 -0
- data/test/dummy/test/fixtures/posts.yml +11 -0
- data/test/dummy/test/functional/posts_controller_test.rb +49 -0
- data/test/dummy/test/unit/admin_user_test.rb +7 -0
- data/test/dummy/test/unit/helpers/posts_helper_test.rb +4 -0
- data/test/dummy/test/unit/post_test.rb +7 -0
- data/test/fixtures/rich/rich_images.yml +11 -0
- data/test/functional/rich/files_controller_test.rb +9 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/rich_test.rb +7 -0
- data/test/test_helper.rb +10 -0
- data/test/unit/helpers/rich/files_helper_test.rb +6 -0
- data/test/unit/rich/rich_image_test.rb +9 -0
- data/vendor/assets/images/ckeditor/plugins/MediaEmbed/icons/mediaembed.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/MediaEmbed/images/icon.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/about/dialogs/hidpi/logo_ckeditor.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/about/dialogs/logo_ckeditor.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/fakeobjects/images/spacer.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/flash/images/placeholder.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/forms/images/hiddenfield.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/icons.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/icons_hidpi.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/iframe/images/placeholder.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/image/images/noimage.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/link/images/anchor.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/link/images/hidpi/anchor.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/magicline/images/hidpi/icon.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/magicline/images/icon.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/maximize/icons/maximize.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/pagebreak/images/pagebreak.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/icons/hidpi/showblocks-rtl.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/icons/hidpi/showblocks.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/icons/showblocks-rtl.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/icons/showblocks.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_address.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_blockquote.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_div.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h1.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h2.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h3.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h4.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h5.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_h6.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_p.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/showblocks/images/block_pre.png +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/angel_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/angry_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/broken_heart.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/confused_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/cry_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/devil_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/embaressed_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/embarrassed_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/envelope.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/heart.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/kiss.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/lightbulb.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/omg_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/regular_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/sad_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/shades_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/teeth_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/thumbs_down.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/thumbs_up.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/tongue_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/tounge_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/whatchutalkingabout_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/smiley/images/wink_smile.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/templates/templates/images/template1.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/templates/templates/images/template2.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/templates/templates/images/template3.gif +0 -0
- data/vendor/assets/images/ckeditor/plugins/widget/images/handle.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/icons.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/dialog_sides.gif +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/dialog_sides.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/dialog_sides_rtl.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/mini.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/noimage.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/sprites.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/sprites_ie6.png +0 -0
- data/vendor/assets/images/ckeditor/skins/BootstrapCK/images/toolbar_start.gif +0 -0
- data/vendor/assets/images/ckeditor/skins/moono/icons.png +0 -0
- data/vendor/assets/images/ckeditor/skins/moono/images/arrow.png +0 -0
- data/vendor/assets/images/ckeditor/skins/moono/images/close.png +0 -0
- data/vendor/assets/images/ckeditor/skins/moono/images/mini.png +0 -0
- data/vendor/assets/javascripts/ckeditor/build-config.js +102 -0
- data/vendor/assets/javascripts/ckeditor/ckeditor.js +926 -0
- data/vendor/assets/javascripts/ckeditor/config.js +10 -0
- data/vendor/assets/javascripts/ckeditor/lang/af.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ar.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/bg.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/bn.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/bs.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ca.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/cs.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/cy.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/da.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/de.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/el.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/en-au.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/en-ca.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/en-gb.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/en.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/eo.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/es.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/et.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/eu.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/fa.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/fi.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/fo.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/fr-ca.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/fr.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/gl.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/gu.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/he.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/hi.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/hr.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/hu.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/id.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/is.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/it.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ja.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ka.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/km.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ko.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ku.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/lt.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/lv.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/mk.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/mn.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ms.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/nb.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/nl.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/no.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/pl.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/pt-br.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/pt.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ro.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ru.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/si.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sk.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sl.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sq.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sr-latn.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sr.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/sv.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/th.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/tr.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/tt.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/ug.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/uk.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/vi.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/zh-cn.js +5 -0
- data/vendor/assets/javascripts/ckeditor/lang/zh.js +5 -0
- data/vendor/assets/javascripts/ckeditor/plugins/MediaEmbed/plugin.js +65 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/_translationstatus.txt +25 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ar.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/bg.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ca.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/cs.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/cy.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/da.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/de.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/el.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/en-gb.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/en.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/eo.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/es.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/et.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/fa.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/fi.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/fr-ca.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/fr.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/gl.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/gu.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/he.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/hi.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/hr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/hu.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/id.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/it.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ja.js +9 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/km.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ko.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ku.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/lt.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/lv.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/mk.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/mn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/nb.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/nl.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/no.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/pl.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/pt-br.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/pt.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ro.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ru.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/si.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sk.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sl.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sq.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sr-latn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/sv.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/th.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/tr.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/tt.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/ug.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/uk.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/vi.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/zh-cn.js +9 -0
- data/vendor/assets/javascripts/ckeditor/plugins/a11yhelp/dialogs/lang/zh.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/about/dialogs/about.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/autogrow/plugin.js +178 -0
- data/vendor/assets/javascripts/ckeditor/plugins/clipboard/dialogs/paste.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/dialog/dialog.js +3 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/edit/closebrackets.js +2 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/edit/closetag.js +3 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/edit/continuelist.js +1 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/edit/matchbrackets.js +4 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/foldcode/foldcode.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/format/autoFormatAll.js +2 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/format/formatting.js +5 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/search/match-highlighter.js +2 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/search/search.js +5 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/addon/search/searchcursor.js +5 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/codemirror.addons.min.js +25 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/codemirror.js +188 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/codemirror.min.js +187 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/codemirror.modes.min.js +25 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/codemirror.search-addons.min.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/mode/css.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/mode/htmlmixed.js +5 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/mode/javascript.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/codemirror/js/mode/xml.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/colordialog/dialogs/colordialog.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/dialog/dialogDefinition.js +4 -0
- data/vendor/assets/javascripts/ckeditor/plugins/div/dialogs/div.js +9 -0
- data/vendor/assets/javascripts/ckeditor/plugins/divarea/plugin.js +36 -0
- data/vendor/assets/javascripts/ckeditor/plugins/find/dialogs/find.js +24 -0
- data/vendor/assets/javascripts/ckeditor/plugins/flash/dialogs/flash.js +23 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/button.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/checkbox.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/form.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/hiddenfield.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/radio.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/select.js +20 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/textarea.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/forms/dialogs/textfield.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/iframe/dialogs/iframe.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/image/dialogs/image.js +43 -0
- data/vendor/assets/javascripts/ckeditor/plugins/lineutils/plugin.js +933 -0
- data/vendor/assets/javascripts/ckeditor/plugins/link/dialogs/anchor.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/link/dialogs/link.js +26 -0
- data/vendor/assets/javascripts/ckeditor/plugins/liststyle/dialogs/liststyle.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/af.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ar.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/bg.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/bn.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/bs.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ca.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/cs.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/cy.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/da.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/de.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/el.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/en-au.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/en-ca.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/en-gb.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/en.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/eo.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/es.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/et.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/eu.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/fa.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/fi.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/fo.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/fr-ca.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/fr.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/gl.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/gu.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/he.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/hi.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/hr.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/hu.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/is.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/it.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ja.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ka.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/km.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ko.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ku.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/lt.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/lv.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/mk.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/mn.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ms.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/nb.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/nl.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/no.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/pl.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/pt-br.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/pt.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ro.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ru.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/sk.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/sl.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/sr-latn.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/sr.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/sv.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/th.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/tr.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/ug.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/uk.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/vi.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/zh-cn.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/lang/zh.js +8 -0
- data/vendor/assets/javascripts/ckeditor/plugins/maximize/plugin.js +301 -0
- data/vendor/assets/javascripts/ckeditor/plugins/pastefromword/filter/default.js +31 -0
- data/vendor/assets/javascripts/ckeditor/plugins/richfile/plugin.js.erb +85 -0
- data/vendor/assets/javascripts/ckeditor/plugins/scayt/dialogs/options.js +17 -0
- data/vendor/assets/javascripts/ckeditor/plugins/scayt/dialogs/toolbar.css +71 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/af.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ar.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/bg.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/bn.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/bs.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ca.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/cs.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/cy.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/da.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/de.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/el.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/en-au.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/en-ca.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/en-gb.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/en.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/eo.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/es.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/et.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/eu.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/fa.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/fi.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/fo.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/fr-ca.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/fr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/gl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/gu.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/he.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/hi.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/hr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/hu.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/id.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/is.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/it.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ja.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ka.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/km.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ko.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ku.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/lt.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/lv.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/mk.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/mn.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ms.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/nb.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/nl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/no.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/pl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/pt-br.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/pt.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ro.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ru.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/si.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sk.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sq.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sr-latn.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/sv.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/th.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/tr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/tt.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/ug.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/uk.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/vi.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/zh-cn.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/lang/zh.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/showblocks/plugin.js +153 -0
- data/vendor/assets/javascripts/ckeditor/plugins/smiley/dialogs/smiley.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/_translationstatus.txt +20 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ar.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/bg.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ca.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/cs.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/cy.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/de.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/el.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/en-gb.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/en.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/eo.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/es.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/et.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/fa.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/fi.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/fr-ca.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/fr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/gl.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/he.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/hr.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/hu.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/id.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/it.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ja.js +9 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/km.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ku.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/lv.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/nb.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/nl.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/no.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/pl.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/pt-br.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/pt.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ru.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/si.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/sk.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/sl.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/sq.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/sv.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/th.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/tr.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/tt.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/ug.js +13 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/uk.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/vi.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/zh-cn.js +9 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/lang/zh.js +12 -0
- data/vendor/assets/javascripts/ckeditor/plugins/specialchar/dialogs/specialchar.js +14 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/af.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ar.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/bg.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/bn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/bs.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ca.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/cs.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/cy.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/da.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/de.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/el.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/en-au.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/en-ca.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/en-gb.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/en.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/eo.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/es.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/et.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/eu.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/fa.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/fi.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/fo.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/fr-ca.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/fr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/gl.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/gu.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/he.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/hi.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/hr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/hu.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/is.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/it.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ja.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ka.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/km.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ko.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ku.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/lt.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/lv.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/mk.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/mn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ms.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/nb.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/nl.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/no.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/pl.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/pt-br.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/pt.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ro.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ru.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/sk.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/sl.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/sr-latn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/sr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/sv.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/th.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/tr.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/ug.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/uk.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/vi.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/zh-cn.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/lang/zh.js +11 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylescombo/plugin.js +186 -0
- data/vendor/assets/javascripts/ckeditor/plugins/stylesheetparser/plugin.js +152 -0
- data/vendor/assets/javascripts/ckeditor/plugins/table/dialogs/table.js +21 -0
- data/vendor/assets/javascripts/ckeditor/plugins/tabletools/dialogs/tableCell.js +17 -0
- data/vendor/assets/javascripts/ckeditor/plugins/templates/dialogs/templates.js +10 -0
- data/vendor/assets/javascripts/ckeditor/plugins/templates/templates/default.js +6 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/dev/console.js +129 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/dev/contents.css +23 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/dev/widgetstyles.html +145 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/images/handle.png +0 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/ar.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/ca.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/cs.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/cy.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/de.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/el.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/en-gb.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/en.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/eo.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/es.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/fa.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/fi.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/fr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/gl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/he.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/hr.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/hu.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/it.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/ja.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/km.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/ko.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/nb.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/nl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/no.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/pl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/pt-br.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/pt.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/ru.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/sl.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/sv.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/tt.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/uk.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/vi.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/zh-cn.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/lang/zh.js +7 -0
- data/vendor/assets/javascripts/ckeditor/plugins/widget/plugin.js +3684 -0
- data/vendor/assets/javascripts/ckeditor/plugins/wsc/dialogs/ciframe.html +66 -0
- data/vendor/assets/javascripts/ckeditor/plugins/wsc/dialogs/tmpFrameset.html +52 -0
- data/vendor/assets/javascripts/ckeditor/plugins/wsc/dialogs/wsc.css +82 -0
- data/vendor/assets/javascripts/ckeditor/plugins/wsc/dialogs/wsc.js +74 -0
- data/vendor/assets/javascripts/ckeditor/plugins/wsc/dialogs/wsc_ie.js +11 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/README.md +26 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/dialog.css +616 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/editor.css +1088 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/icons.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/dialog_sides.gif +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/dialog_sides.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/dialog_sides_rtl.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/mini.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/noimage.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/sprites.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/sprites_ie6.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/images/toolbar_start.gif +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/skin.js +7 -0
- data/vendor/assets/javascripts/ckeditor/skins/BootstrapCK/templates.css +54 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/dialog.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/dialog_ie.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/dialog_ie7.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/dialog_ie8.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/dialog_iequirks.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor_gecko.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor_ie.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor_ie7.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor_ie8.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/editor_iequirks.css +5 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/icons.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/icons_hidpi.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/arrow.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/close.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/hidpi/close.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/hidpi/lock-open.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/hidpi/lock.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/hidpi/refresh.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/lock-open.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/lock.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/images/refresh.png +0 -0
- data/vendor/assets/javascripts/ckeditor/skins/moono/readme.md +51 -0
- data/vendor/assets/javascripts/ckeditor/styles.js +111 -0
- data/vendor/assets/javascripts/fileuploader.js +1247 -0
- data/vendor/assets/stylesheets/ckeditor/contents.css +99 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/css/codemirror.ckeditor.css +74 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/css/codemirror.css +245 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/css/codemirror.min.css +1 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/ambiance-mobile.css +6 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/ambiance.css +76 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/blackboard.css +25 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/cobalt.css +18 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/eclipse.css +25 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/elegant.css +10 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/erlang-dark.css +21 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/lesser-dark.css +44 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/monokai.css +28 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/neat.css +9 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/night.css +21 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/rubyblue.css +21 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/solarized.css +207 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/twilight.css +25 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/vibrant-ink.css +27 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/codemirror/theme/xq-dark.css +46 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/scayt/dialogs/toolbar.css +71 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/templates/dialogs/templates.css +84 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/widget/dev/contents.css +23 -0
- data/vendor/assets/stylesheets/ckeditor/plugins/wsc/dialogs/wsc.css +82 -0
- data/vendor/assets/stylesheets/ckeditor/skins/BootstrapCK/dialog.css +616 -0
- data/vendor/assets/stylesheets/ckeditor/skins/BootstrapCK/editor.css +1088 -0
- data/vendor/assets/stylesheets/ckeditor/skins/BootstrapCK/templates.css +54 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog_ie.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog_ie7.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog_ie8.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog_iequirks.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/dialog_opera.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor_gecko.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor_ie.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor_ie7.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor_ie8.css +5 -0
- data/vendor/assets/stylesheets/ckeditor/skins/moono/editor_iequirks.css +5 -0
- metadata +955 -0
|
@@ -0,0 +1,3684 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileOverview [Widget](http://ckeditor.com/addon/widget) plugin.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
( function() {
|
|
13
|
+
|
|
14
|
+
var DRAG_HANDLER_SIZE = 15;
|
|
15
|
+
|
|
16
|
+
CKEDITOR.plugins.add( 'widget', {
|
|
17
|
+
lang: 'ar,ca,cs,cy,de,el,en,en-gb,eo,es,fa,fi,fr,gl,he,hr,hu,it,ja,km,ko,nb,nl,no,pl,pt,pt-br,ru,sl,sv,tt,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
|
|
18
|
+
requires: 'lineutils,clipboard',
|
|
19
|
+
onLoad: function() {
|
|
20
|
+
CKEDITOR.addCss(
|
|
21
|
+
'.cke_widget_wrapper{' +
|
|
22
|
+
'position:relative;' +
|
|
23
|
+
'outline:none' +
|
|
24
|
+
'}' +
|
|
25
|
+
'.cke_widget_inline{' +
|
|
26
|
+
'display:inline-block' +
|
|
27
|
+
'}' +
|
|
28
|
+
'.cke_widget_wrapper:hover>.cke_widget_element{' +
|
|
29
|
+
'outline:2px solid yellow;' +
|
|
30
|
+
'cursor:default' +
|
|
31
|
+
'}' +
|
|
32
|
+
'.cke_widget_wrapper:hover .cke_widget_editable{' +
|
|
33
|
+
'outline:2px solid yellow' +
|
|
34
|
+
'}' +
|
|
35
|
+
'.cke_widget_wrapper.cke_widget_focused>.cke_widget_element,' +
|
|
36
|
+
// We need higher specificity than hover style.
|
|
37
|
+
'.cke_widget_wrapper .cke_widget_editable.cke_widget_editable_focused{' +
|
|
38
|
+
'outline:2px solid #ace' +
|
|
39
|
+
'}' +
|
|
40
|
+
'.cke_widget_editable{' +
|
|
41
|
+
'cursor:text' +
|
|
42
|
+
'}' +
|
|
43
|
+
'.cke_widget_drag_handler_container{' +
|
|
44
|
+
'position:absolute;' +
|
|
45
|
+
'width:' + DRAG_HANDLER_SIZE + 'px;' +
|
|
46
|
+
'height:0;' +
|
|
47
|
+
// Initially drag handler should not be visible, until its position will be
|
|
48
|
+
// repositioned. #11177
|
|
49
|
+
'left:-9999px;' +
|
|
50
|
+
'opacity:0.75;' +
|
|
51
|
+
'transition:height 0s 0.2s;' + // Delay hiding drag handler.
|
|
52
|
+
// Prevent drag handler from being misplaced (#11198).
|
|
53
|
+
'line-height:0' +
|
|
54
|
+
'}' +
|
|
55
|
+
'.cke_widget_wrapper:hover>.cke_widget_drag_handler_container{' +
|
|
56
|
+
'height:' + DRAG_HANDLER_SIZE + 'px;' +
|
|
57
|
+
'transition:none' +
|
|
58
|
+
'}' +
|
|
59
|
+
'.cke_widget_drag_handler_container:hover{' +
|
|
60
|
+
'opacity:1' +
|
|
61
|
+
'}'+
|
|
62
|
+
'img.cke_widget_drag_handler{' +
|
|
63
|
+
'cursor:move;' +
|
|
64
|
+
'width:' + DRAG_HANDLER_SIZE + 'px;' +
|
|
65
|
+
'height:' + DRAG_HANDLER_SIZE + 'px;' +
|
|
66
|
+
'display:inline-block' +
|
|
67
|
+
'}' +
|
|
68
|
+
'.cke_widget_mask{' +
|
|
69
|
+
'position:absolute;' +
|
|
70
|
+
'top:0;' +
|
|
71
|
+
'left:0;' +
|
|
72
|
+
'width:100%;' +
|
|
73
|
+
'height:100%;' +
|
|
74
|
+
'display:block' +
|
|
75
|
+
'}' +
|
|
76
|
+
'.cke_editable.cke_widget_dragging, .cke_editable.cke_widget_dragging *{' +
|
|
77
|
+
'cursor:move !important' +
|
|
78
|
+
'}'
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
beforeInit: function( editor ) {
|
|
83
|
+
/**
|
|
84
|
+
* An instance of widget repository. It contains all
|
|
85
|
+
* {@link CKEDITOR.plugins.widget.repository#registered registered widget definitions} and
|
|
86
|
+
* {@link CKEDITOR.plugins.widget.repository#instances initialized instances}.
|
|
87
|
+
*
|
|
88
|
+
* editor.widgets.add( 'someName', {
|
|
89
|
+
* // Widget definition...
|
|
90
|
+
* } );
|
|
91
|
+
*
|
|
92
|
+
* editor.widgets.registered.someName; // -> Widget definition
|
|
93
|
+
*
|
|
94
|
+
* @since 4.3
|
|
95
|
+
* @readonly
|
|
96
|
+
* @property {CKEDITOR.plugins.widget.repository} widgets
|
|
97
|
+
* @member CKEDITOR.editor
|
|
98
|
+
*/
|
|
99
|
+
editor.widgets = new Repository( editor );
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
afterInit: function( editor ) {
|
|
103
|
+
addWidgetButtons( editor );
|
|
104
|
+
setupContextMenu( editor );
|
|
105
|
+
}
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Widget repository. It keeps track of all {@link #registered registered widget definitions} and
|
|
110
|
+
* {@link #instances initialized instances}. An instance of the repository is available under
|
|
111
|
+
* the {@link CKEDITOR.editor#widgets} property.
|
|
112
|
+
*
|
|
113
|
+
* @class CKEDITOR.plugins.widget.repository
|
|
114
|
+
* @mixins CKEDITOR.event
|
|
115
|
+
* @constructor Creates a widget repository instance. Note that the widget plugin automatically
|
|
116
|
+
* creates a repository instance which is available under the {@link CKEDITOR.editor#widgets} property.
|
|
117
|
+
* @param {CKEDITOR.editor} editor The editor instance for which the repository will be created.
|
|
118
|
+
*/
|
|
119
|
+
function Repository( editor ) {
|
|
120
|
+
/**
|
|
121
|
+
* The editor instance for which this repository was created.
|
|
122
|
+
*
|
|
123
|
+
* @readonly
|
|
124
|
+
* @property {CKEDITOR.editor} editor
|
|
125
|
+
*/
|
|
126
|
+
this.editor = editor;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* A hash of registered widget definitions (definition name => {@link CKEDITOR.plugins.widget.definition}).
|
|
130
|
+
*
|
|
131
|
+
* To register a definition use the {@link #add} method.
|
|
132
|
+
*
|
|
133
|
+
* @readonly
|
|
134
|
+
*/
|
|
135
|
+
this.registered = {};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* An object containing initialized widget instances (widget id => {@link CKEDITOR.plugins.widget}).
|
|
139
|
+
*
|
|
140
|
+
* @readonly
|
|
141
|
+
*/
|
|
142
|
+
this.instances = {};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* An array of selected widget instances.
|
|
146
|
+
*
|
|
147
|
+
* @readonly
|
|
148
|
+
* @property {CKEDITOR.plugins.widget[]} selected
|
|
149
|
+
*/
|
|
150
|
+
this.selected = [];
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The focused widget instance. See also {@link CKEDITOR.plugins.widget#event-focus}
|
|
154
|
+
* and {@link CKEDITOR.plugins.widget#event-blur} events.
|
|
155
|
+
*
|
|
156
|
+
* editor.on( 'selectionChange', function() {
|
|
157
|
+
* if ( editor.widgets.focused ) {
|
|
158
|
+
* // Do something when a widget is focused...
|
|
159
|
+
* }
|
|
160
|
+
* } );
|
|
161
|
+
*
|
|
162
|
+
* @readonly
|
|
163
|
+
* @property {CKEDITOR.plugins.widget} focused
|
|
164
|
+
*/
|
|
165
|
+
this.focused = null;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The widget instance that contains the nested editable which is currently focused.
|
|
169
|
+
*
|
|
170
|
+
* @readonly
|
|
171
|
+
* @property {CKEDITOR.plugins.widget} widgetHoldingFocusedEditable
|
|
172
|
+
*/
|
|
173
|
+
this.widgetHoldingFocusedEditable = null;
|
|
174
|
+
|
|
175
|
+
this._ = {
|
|
176
|
+
nextId: 0,
|
|
177
|
+
upcasts: [],
|
|
178
|
+
upcastCallbacks: [],
|
|
179
|
+
filters: {}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
setupWidgetsLifecycle( this );
|
|
183
|
+
setupSelectionObserver( this );
|
|
184
|
+
setupMouseObserver( this );
|
|
185
|
+
setupKeyboardObserver( this );
|
|
186
|
+
setupDragAndDrop( this );
|
|
187
|
+
setupNativeCutAndCopy( this );
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
Repository.prototype = {
|
|
191
|
+
/**
|
|
192
|
+
* Minimum interval between selection checks.
|
|
193
|
+
*
|
|
194
|
+
* @private
|
|
195
|
+
*/
|
|
196
|
+
MIN_SELECTION_CHECK_INTERVAL: 500,
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Adds a widget definition to the repository. Fires the {@link CKEDITOR.editor#widgetDefinition} event
|
|
200
|
+
* which allows to modify the widget definition which is going to be registered.
|
|
201
|
+
*
|
|
202
|
+
* @param {String} name The name of the widget definition.
|
|
203
|
+
* @param {CKEDITOR.plugins.widget.definition} widgetDef Widget definition.
|
|
204
|
+
* @returns {CKEDITOR.plugins.widget.definition}
|
|
205
|
+
*/
|
|
206
|
+
add: function( name, widgetDef ) {
|
|
207
|
+
// Create prototyped copy of original widget definition, so we won't modify it.
|
|
208
|
+
widgetDef = CKEDITOR.tools.prototypedCopy( widgetDef );
|
|
209
|
+
widgetDef.name = name;
|
|
210
|
+
|
|
211
|
+
widgetDef._ = widgetDef._ || {};
|
|
212
|
+
|
|
213
|
+
this.editor.fire( 'widgetDefinition', widgetDef );
|
|
214
|
+
|
|
215
|
+
if ( widgetDef.template )
|
|
216
|
+
widgetDef.template = new CKEDITOR.template( widgetDef.template );
|
|
217
|
+
|
|
218
|
+
addWidgetCommand( this.editor, widgetDef );
|
|
219
|
+
addWidgetProcessors( this, widgetDef );
|
|
220
|
+
|
|
221
|
+
this.registered[ name ] = widgetDef;
|
|
222
|
+
|
|
223
|
+
return widgetDef;
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Adds a callback for element upcasting. Each callback will be executed
|
|
228
|
+
* for every element which is later tested by upcast methods. If a callback
|
|
229
|
+
* returns `false`, the element will not be upcasted.
|
|
230
|
+
*
|
|
231
|
+
* // Images with the "banner" class will not be upcasted (e.g. to the image widget).
|
|
232
|
+
* editor.widgets.addUpcastCallback( function( element ) {
|
|
233
|
+
* if ( element.name == 'img' && element.hasClass( 'banner' ) )
|
|
234
|
+
* return false;
|
|
235
|
+
* } );
|
|
236
|
+
*
|
|
237
|
+
* @param {Function} callback
|
|
238
|
+
* @param {CKEDITOR.htmlParser.element} callback.element
|
|
239
|
+
*/
|
|
240
|
+
addUpcastCallback: function( callback ) {
|
|
241
|
+
this._.upcastCallbacks.push( callback );
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Checks the selection to update widget states (selection and focus).
|
|
246
|
+
*
|
|
247
|
+
* This method is triggered by the {@link #event-checkSelection} event.
|
|
248
|
+
*/
|
|
249
|
+
checkSelection: function() {
|
|
250
|
+
var sel = this.editor.getSelection(),
|
|
251
|
+
selectedElement = sel.getSelectedElement(),
|
|
252
|
+
updater = stateUpdater( this ),
|
|
253
|
+
widget;
|
|
254
|
+
|
|
255
|
+
// Widget is focused so commit and finish checking.
|
|
256
|
+
if ( selectedElement && ( widget = this.getByElement( selectedElement, true ) ) )
|
|
257
|
+
return updater.focus( widget ).select( widget ).commit();
|
|
258
|
+
|
|
259
|
+
var range = sel.getRanges()[ 0 ];
|
|
260
|
+
|
|
261
|
+
// No ranges or collapsed range mean that nothing is selected, so commit and finish checking.
|
|
262
|
+
if ( !range || range.collapsed )
|
|
263
|
+
return updater.commit();
|
|
264
|
+
|
|
265
|
+
// Range is not empty, so create walker checking for wrappers.
|
|
266
|
+
var walker = new CKEDITOR.dom.walker( range ),
|
|
267
|
+
wrapper;
|
|
268
|
+
|
|
269
|
+
walker.evaluator = isDomWidgetWrapper;
|
|
270
|
+
|
|
271
|
+
while ( ( wrapper = walker.next() ) )
|
|
272
|
+
updater.select( this.getByElement( wrapper ) );
|
|
273
|
+
|
|
274
|
+
updater.commit();
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Checks if all widget instances are still present in the DOM.
|
|
279
|
+
* Destroys those instances that are not present.
|
|
280
|
+
* Reinitializes widgets on widget wrappers for which widget instances
|
|
281
|
+
* cannot be found.
|
|
282
|
+
*
|
|
283
|
+
* This method triggers the {@link #event-checkWidgets} event whose listeners
|
|
284
|
+
* can cancel the method's execution or modify its options.
|
|
285
|
+
*
|
|
286
|
+
* @param [options] The options object.
|
|
287
|
+
* @param {Boolean} [options.initOnlyNew] Initializes widgets only on newly wrapped
|
|
288
|
+
* widget elements (those which still have the `cke_widget_new` class). When this option is
|
|
289
|
+
* set to `true`, widgets which were invalidated (e.g. by replacing with a cloned DOM structure)
|
|
290
|
+
* will not be reinitialized. This makes the check faster.
|
|
291
|
+
* @param {Boolean} [options.focusInited] If only one widget is initialized by
|
|
292
|
+
* the method, it will be focused.
|
|
293
|
+
*/
|
|
294
|
+
checkWidgets: function( options ) {
|
|
295
|
+
this.fire( 'checkWidgets', CKEDITOR.tools.copy( options || {} ) );
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Removes the widget from the editor and moves the selection to the closest
|
|
300
|
+
* editable position if the widget was focused before.
|
|
301
|
+
*
|
|
302
|
+
* @param {CKEDITOR.plugins.widget} widget The widget instance to be deleted.
|
|
303
|
+
*/
|
|
304
|
+
del: function( widget ) {
|
|
305
|
+
if ( this.focused === widget ) {
|
|
306
|
+
var editor = widget.editor,
|
|
307
|
+
range = editor.createRange(),
|
|
308
|
+
found;
|
|
309
|
+
|
|
310
|
+
// If haven't found place for caret on the default side,
|
|
311
|
+
// try to find it on the other side.
|
|
312
|
+
if ( !( found = range.moveToClosestEditablePosition( widget.wrapper, true ) ) )
|
|
313
|
+
found = range.moveToClosestEditablePosition( widget.wrapper, false );
|
|
314
|
+
|
|
315
|
+
if ( found )
|
|
316
|
+
editor.getSelection().selectRanges( [ range ] );
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
widget.wrapper.remove();
|
|
320
|
+
this.destroy( widget, true );
|
|
321
|
+
},
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Destroys the widget instance.
|
|
325
|
+
*
|
|
326
|
+
* @param {CKEDITOR.plugins.widget} widget The widget instance to be destroyed.
|
|
327
|
+
* @param {Boolean} [offline] Whether the widget is offline (detached from the DOM tree) —
|
|
328
|
+
* in this case the DOM (attributes, classes, etc.) will not be cleaned up.
|
|
329
|
+
*/
|
|
330
|
+
destroy: function( widget, offline ) {
|
|
331
|
+
if ( this.widgetHoldingFocusedEditable === widget )
|
|
332
|
+
setFocusedEditable( this, widget, null, offline );
|
|
333
|
+
|
|
334
|
+
widget.destroy( offline );
|
|
335
|
+
delete this.instances[ widget.id ];
|
|
336
|
+
this.fire( 'instanceDestroyed', widget );
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Destroys all widget instances.
|
|
341
|
+
*
|
|
342
|
+
* @param {Boolean} [offline] Whether the widgets are offline (detached from the DOM tree) —
|
|
343
|
+
* in this case the DOM (attributes, classes, etc.) will not be cleaned up.
|
|
344
|
+
*/
|
|
345
|
+
destroyAll: function( offline ) {
|
|
346
|
+
var instances = this.instances,
|
|
347
|
+
widget;
|
|
348
|
+
|
|
349
|
+
for ( var id in instances ) {
|
|
350
|
+
widget = instances[ id ];
|
|
351
|
+
this.destroy( widget, offline );
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Finalizes a process of widget creation. This includes:
|
|
357
|
+
*
|
|
358
|
+
* * inserting widget element into editor,
|
|
359
|
+
* * marking widget instance as ready (see {@link CKEDITOR.plugins.widget#event-ready}),
|
|
360
|
+
* * focusing widget instance.
|
|
361
|
+
*
|
|
362
|
+
* This method is used by the default widget's command and is called
|
|
363
|
+
* after widget's dialog (if set) is closed. It may also be used in a
|
|
364
|
+
* customized process of widget creation and insertion.
|
|
365
|
+
*
|
|
366
|
+
* widget.once( 'edit', function() {
|
|
367
|
+
* // Finalize creation only of not ready widgets.
|
|
368
|
+
* if ( widget.isReady() )
|
|
369
|
+
* return;
|
|
370
|
+
*
|
|
371
|
+
* // Cancel edit event to prevent automatic widget insertion.
|
|
372
|
+
* evt.cancel();
|
|
373
|
+
*
|
|
374
|
+
* CustomDialog.open( widget.data, function saveCallback( savedData ) {
|
|
375
|
+
* // Cache the container, because widget may be destroyed while saving data,
|
|
376
|
+
* // if this process will require some deep transformations.
|
|
377
|
+
* var container = widget.wrapper.getParent();
|
|
378
|
+
*
|
|
379
|
+
* widget.setData( savedData );
|
|
380
|
+
*
|
|
381
|
+
* // Widget will be retrieved from container and inserted into editor.
|
|
382
|
+
* editor.widgets.finalizeCreation( container );
|
|
383
|
+
* } );
|
|
384
|
+
* } );
|
|
385
|
+
*
|
|
386
|
+
* @param {CKEDITOR.dom.element/CKEDITOR.dom.documentFragment} container The element
|
|
387
|
+
* or document fragment which contains widget wrapper. The container is used, so before
|
|
388
|
+
* finalizing creation the widget can be freely transformed (even destroyed and reinitialized).
|
|
389
|
+
*/
|
|
390
|
+
finalizeCreation: function( container ) {
|
|
391
|
+
var wrapper = container.getFirst();
|
|
392
|
+
if ( wrapper && isDomWidgetWrapper( wrapper ) ) {
|
|
393
|
+
this.editor.insertElement( wrapper );
|
|
394
|
+
|
|
395
|
+
var widget = this.getByElement( wrapper );
|
|
396
|
+
// Fire postponed #ready event.
|
|
397
|
+
widget.ready = true;
|
|
398
|
+
widget.fire( 'ready' );
|
|
399
|
+
widget.focus();
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Finds a widget instance which contains a given element. The element will be the {@link CKEDITOR.plugins.widget#wrapper wrapper}
|
|
405
|
+
* of the returned widget or a descendant of this {@link CKEDITOR.plugins.widget#wrapper wrapper}.
|
|
406
|
+
*
|
|
407
|
+
* editor.widgets.getByElement( someWidget.wrapper ); // -> someWidget
|
|
408
|
+
* editor.widgets.getByElement( someWidget.parts.caption ); // -> someWidget
|
|
409
|
+
*
|
|
410
|
+
* // Check wrapper only:
|
|
411
|
+
* editor.widgets.getByElement( someWidget.wrapper, true ); // -> someWidget
|
|
412
|
+
* editor.widgets.getByElement( someWidget.parts.caption, true ); // -> null
|
|
413
|
+
*
|
|
414
|
+
* @param {CKEDITOR.dom.element} element The element to be checked.
|
|
415
|
+
* @param {Boolean} [checkWrapperOnly] If set to `true`, the method will not check wrappers' descendants.
|
|
416
|
+
* @returns {CKEDITOR.plugins.widget} The widget instance or `null`.
|
|
417
|
+
*/
|
|
418
|
+
getByElement: function( element, checkWrapperOnly ) {
|
|
419
|
+
if ( !element )
|
|
420
|
+
return null;
|
|
421
|
+
|
|
422
|
+
var wrapper;
|
|
423
|
+
|
|
424
|
+
for ( var id in this.instances ) {
|
|
425
|
+
wrapper = this.instances[ id ].wrapper;
|
|
426
|
+
if ( wrapper.equals( element ) || ( !checkWrapperOnly && wrapper.contains( element ) ) )
|
|
427
|
+
return this.instances[ id ];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return null;
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Initializes a widget on a given element if the widget has not been initialized on it yet.
|
|
435
|
+
*
|
|
436
|
+
* @param {CKEDITOR.dom.element} element The future widget element.
|
|
437
|
+
* @param {String/CKEDITOR.plugins.widget.definition} [widgetDef] Name of a widget or a widget definition.
|
|
438
|
+
* The widget definition should be previously registered by using the
|
|
439
|
+
* {@link CKEDITOR.plugins.widget.repository#add} method.
|
|
440
|
+
* @param [startupData] Widget startup data (has precedence over default one).
|
|
441
|
+
* @returns {CKEDITOR.plugins.widget} The widget instance or `null` if a widget could not be initialized on
|
|
442
|
+
* a given element.
|
|
443
|
+
*/
|
|
444
|
+
initOn: function( element, widgetDef, startupData ) {
|
|
445
|
+
if ( !widgetDef )
|
|
446
|
+
widgetDef = this.registered[ element.data( 'widget' ) ];
|
|
447
|
+
else if ( typeof widgetDef == 'string' )
|
|
448
|
+
widgetDef = this.registered[ widgetDef ];
|
|
449
|
+
|
|
450
|
+
if ( !widgetDef )
|
|
451
|
+
return null;
|
|
452
|
+
|
|
453
|
+
// Wrap element if still wasn't wrapped (was added during runtime by method that skips dataProcessor).
|
|
454
|
+
var wrapper = this.wrapElement( element, widgetDef.name );
|
|
455
|
+
|
|
456
|
+
if ( wrapper ) {
|
|
457
|
+
// Check if widget wrapper is new (widget hasn't been initialized on it yet).
|
|
458
|
+
// This class will be removed by widget constructor to avoid locking snapshot twice.
|
|
459
|
+
if ( wrapper.hasClass( 'cke_widget_new' ) ) {
|
|
460
|
+
var widget = new Widget( this, this._.nextId++, element, widgetDef, startupData );
|
|
461
|
+
|
|
462
|
+
// Widget could be destroyed when initializing it.
|
|
463
|
+
if ( widget.isInited() ) {
|
|
464
|
+
this.instances[ widget.id ] = widget;
|
|
465
|
+
|
|
466
|
+
return widget;
|
|
467
|
+
} else
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Widget already has been initialized, so try to get widget by element.
|
|
472
|
+
// Note - it may happen that other instance will returned than the one created above,
|
|
473
|
+
// if for example widget was destroyed and reinitialized.
|
|
474
|
+
return this.getByElement( element );
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// No wrapper means that there's no widget for this element.
|
|
478
|
+
return null;
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Initializes widgets on all elements which were wrapped by {@link #wrapElement} and
|
|
483
|
+
* have not been initialized yet.
|
|
484
|
+
*
|
|
485
|
+
* @param {CKEDITOR.dom.element} [container=editor.editable()] The container which will be checked for not
|
|
486
|
+
* initialized widgets. Defaults to editor's {@link CKEDITOR.editor#editable editable} element.
|
|
487
|
+
* @returns {CKEDITOR.plugins.widget[]} Array of widget instances which have been initialized.
|
|
488
|
+
*/
|
|
489
|
+
initOnAll: function( container ) {
|
|
490
|
+
var newWidgets = ( container || this.editor.editable() ).find( '.cke_widget_new' ),
|
|
491
|
+
newInstances = [],
|
|
492
|
+
instance;
|
|
493
|
+
|
|
494
|
+
for ( var i = newWidgets.count(); i--; ) {
|
|
495
|
+
instance = this.initOn( newWidgets.getItem( i ).getFirst( isDomWidgetElement ) );
|
|
496
|
+
if ( instance )
|
|
497
|
+
newInstances.push( instance );
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return newInstances;
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Parses element classes string and returns an object
|
|
505
|
+
* whose keys contain class names. Skips all `cke_*` classes.
|
|
506
|
+
*
|
|
507
|
+
* This method is used by the {@link CKEDITOR.plugins.widget#getClasses} method and
|
|
508
|
+
* may be used when overriding that method.
|
|
509
|
+
*
|
|
510
|
+
* @since 4.4
|
|
511
|
+
* @param {String} classes String (value of `class` attribute).
|
|
512
|
+
* @returns {Object} Object containing classes or `null` if no classes found.
|
|
513
|
+
*/
|
|
514
|
+
parseElementClasses: function( classes ) {
|
|
515
|
+
if ( !classes )
|
|
516
|
+
return null;
|
|
517
|
+
|
|
518
|
+
classes = CKEDITOR.tools.trim( classes ).split( /\s+/ );
|
|
519
|
+
|
|
520
|
+
var cl,
|
|
521
|
+
obj = {},
|
|
522
|
+
hasClasses = 0;
|
|
523
|
+
|
|
524
|
+
while ( ( cl = classes.pop() ) ) {
|
|
525
|
+
if ( cl.indexOf( 'cke_' ) == -1 )
|
|
526
|
+
obj[ cl ] = hasClasses = 1;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return hasClasses ? obj : null;
|
|
530
|
+
},
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Wraps an element with a widget's non-editable container.
|
|
534
|
+
*
|
|
535
|
+
* If this method is called on an {@link CKEDITOR.htmlParser.element}, then it will
|
|
536
|
+
* also take care of fixing the DOM after wrapping (the wrapper may not be allowed in element's parent).
|
|
537
|
+
*
|
|
538
|
+
* @param {CKEDITOR.dom.element/CKEDITOR.htmlParser.element} element The widget element to be wrapped.
|
|
539
|
+
* @param {String} [widgetName] The name of the widget definition. Defaults to element's `data-widget`
|
|
540
|
+
* attribute value.
|
|
541
|
+
* @returns {CKEDITOR.dom.element/CKEDITOR.htmlParser.element} The wrapper element or `null` if
|
|
542
|
+
* the widget definition of this name is not registered.
|
|
543
|
+
*/
|
|
544
|
+
wrapElement: function( element, widgetName ) {
|
|
545
|
+
var wrapper = null,
|
|
546
|
+
widgetDef,
|
|
547
|
+
isInline;
|
|
548
|
+
|
|
549
|
+
if ( element instanceof CKEDITOR.dom.element ) {
|
|
550
|
+
widgetDef = this.registered[ widgetName || element.data( 'widget' ) ];
|
|
551
|
+
if ( !widgetDef )
|
|
552
|
+
return null;
|
|
553
|
+
|
|
554
|
+
// Do not wrap already wrapped element.
|
|
555
|
+
wrapper = element.getParent();
|
|
556
|
+
if ( wrapper && wrapper.type == CKEDITOR.NODE_ELEMENT && wrapper.data( 'cke-widget-wrapper' ) )
|
|
557
|
+
return wrapper;
|
|
558
|
+
|
|
559
|
+
// If attribute isn't already set (e.g. for pasted widget), set it.
|
|
560
|
+
if ( !element.hasAttribute( 'data-cke-widget-keep-attr' ) )
|
|
561
|
+
element.data( 'cke-widget-keep-attr', element.data( 'widget' ) ? 1 : 0 );
|
|
562
|
+
if ( widgetName )
|
|
563
|
+
element.data( 'widget', widgetName );
|
|
564
|
+
|
|
565
|
+
isInline = isWidgetInline( widgetDef, element.getName() );
|
|
566
|
+
|
|
567
|
+
wrapper = new CKEDITOR.dom.element( isInline ? 'span' : 'div' );
|
|
568
|
+
wrapper.setAttributes( getWrapperAttributes( isInline ) );
|
|
569
|
+
|
|
570
|
+
wrapper.data( 'cke-display-name', widgetDef.pathName ? widgetDef.pathName : element.getName() );
|
|
571
|
+
|
|
572
|
+
// Replace element unless it is a detached one.
|
|
573
|
+
if ( element.getParent( true ) )
|
|
574
|
+
wrapper.replace( element );
|
|
575
|
+
element.appendTo( wrapper );
|
|
576
|
+
}
|
|
577
|
+
else if ( element instanceof CKEDITOR.htmlParser.element ) {
|
|
578
|
+
widgetDef = this.registered[ widgetName || element.attributes[ 'data-widget' ] ];
|
|
579
|
+
if ( !widgetDef )
|
|
580
|
+
return null;
|
|
581
|
+
|
|
582
|
+
wrapper = element.parent;
|
|
583
|
+
if ( wrapper && wrapper.type == CKEDITOR.NODE_ELEMENT && wrapper.attributes[ 'data-cke-widget-wrapper' ] )
|
|
584
|
+
return wrapper;
|
|
585
|
+
|
|
586
|
+
// If attribute isn't already set (e.g. for pasted widget), set it.
|
|
587
|
+
if ( !( 'data-cke-widget-keep-attr' in element.attributes ) )
|
|
588
|
+
element.attributes[ 'data-cke-widget-keep-attr' ] = element.attributes[ 'data-widget' ] ? 1 : 0;
|
|
589
|
+
if ( widgetName )
|
|
590
|
+
element.attributes[ 'data-widget' ] = widgetName;
|
|
591
|
+
|
|
592
|
+
isInline = isWidgetInline( widgetDef, element.name );
|
|
593
|
+
|
|
594
|
+
wrapper = new CKEDITOR.htmlParser.element( isInline ? 'span' : 'div', getWrapperAttributes( isInline ) );
|
|
595
|
+
|
|
596
|
+
wrapper.attributes[ 'data-cke-display-name' ] = widgetDef.pathName ? widgetDef.pathName : element.name;
|
|
597
|
+
|
|
598
|
+
var parent = element.parent,
|
|
599
|
+
index;
|
|
600
|
+
|
|
601
|
+
// Don't detach already detached element.
|
|
602
|
+
if ( parent ) {
|
|
603
|
+
index = element.getIndex();
|
|
604
|
+
element.remove();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
wrapper.add( element );
|
|
608
|
+
|
|
609
|
+
// Insert wrapper fixing DOM (splitting parents if wrapper is not allowed inside them).
|
|
610
|
+
parent && insertElement( parent, index, wrapper );
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return wrapper;
|
|
614
|
+
},
|
|
615
|
+
|
|
616
|
+
// Expose for tests.
|
|
617
|
+
_tests_getNestedEditable: getNestedEditable,
|
|
618
|
+
_tests_createEditableFilter: createEditableFilter
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
CKEDITOR.event.implementOn( Repository.prototype );
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* An event fired when a widget instance is created, but before it is fully initialized.
|
|
625
|
+
*
|
|
626
|
+
* @event instanceCreated
|
|
627
|
+
* @param {CKEDITOR.plugins.widget} data The widget instance.
|
|
628
|
+
*/
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* An event fired when a widget instance was destroyed.
|
|
632
|
+
*
|
|
633
|
+
* See also {@link CKEDITOR.plugins.widget#event-destroy}.
|
|
634
|
+
*
|
|
635
|
+
* @event instanceDestroyed
|
|
636
|
+
* @param {CKEDITOR.plugins.widget} data The widget instance.
|
|
637
|
+
*/
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* An event fired to trigger the selection check.
|
|
641
|
+
*
|
|
642
|
+
* See the {@link #method-checkSelection} method.
|
|
643
|
+
*
|
|
644
|
+
* @event checkSelection
|
|
645
|
+
*/
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* An event fired by the the {@link #method-checkWidgets} method.
|
|
649
|
+
*
|
|
650
|
+
* It can be canceled in order to stop the {@link #method-checkWidgets}
|
|
651
|
+
* method execution or the event listener can modify the method's options.
|
|
652
|
+
*
|
|
653
|
+
* @event checkWidgets
|
|
654
|
+
* @param [data]
|
|
655
|
+
* @param {Boolean} [data.initOnlyNew] Initialize widgets only on newly wrapped
|
|
656
|
+
* widget elements (those which still have the `cke_widget_new` class). When this option is
|
|
657
|
+
* set to `true`, widgets which were invalidated (e.g. by replacing with a cloned DOM structure)
|
|
658
|
+
* will not be reinitialized. This makes the check faster.
|
|
659
|
+
* @param {Boolean} [data.focusInited] If only one widget is initialized by
|
|
660
|
+
* the method, it will be focused.
|
|
661
|
+
*/
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* An instance of a widget. Together with {@link CKEDITOR.plugins.widget.repository} these
|
|
666
|
+
* two classes constitute the core of the Widget System.
|
|
667
|
+
*
|
|
668
|
+
* Note that neither the repository nor the widget instances can be created by using their constructors.
|
|
669
|
+
* A repository instance is automatically set up by the Widget plugin and is accessible under
|
|
670
|
+
* {@link CKEDITOR.editor#widgets}, while widget instances are created and destroyed by the repository.
|
|
671
|
+
*
|
|
672
|
+
* To create a widget, first you need to {@link CKEDITOR.plugins.widget.repository#add register} its
|
|
673
|
+
* {@link CKEDITOR.plugins.widget.definition definition}:
|
|
674
|
+
*
|
|
675
|
+
* editor.widgets.add( 'simplebox', {
|
|
676
|
+
* upcast: function( element ) {
|
|
677
|
+
* // Defines which elements will become widgets.
|
|
678
|
+
* if ( element.hasClass( 'simplebox' ) )
|
|
679
|
+
* return true;
|
|
680
|
+
* },
|
|
681
|
+
* init: function() {
|
|
682
|
+
* // ...
|
|
683
|
+
* }
|
|
684
|
+
* } );
|
|
685
|
+
*
|
|
686
|
+
* Once the widget definition is registered, widgets will be automatically
|
|
687
|
+
* created when loading data:
|
|
688
|
+
*
|
|
689
|
+
* editor.setData( '<div class="simplebox">foo</div>', function() {
|
|
690
|
+
* console.log( editor.widgets.instances ); // -> An object containing one instance.
|
|
691
|
+
* } );
|
|
692
|
+
*
|
|
693
|
+
* It is also possible to create instances during runtime by using a command
|
|
694
|
+
* (if a {@link CKEDITOR.plugins.widget.definition#template} property was defined):
|
|
695
|
+
*
|
|
696
|
+
* // You can execute an automatically defined command to
|
|
697
|
+
* // insert a new simplebox widget or edit the one currently focused.
|
|
698
|
+
* editor.execCommand( 'simplebox' );
|
|
699
|
+
*
|
|
700
|
+
* Or in a completely custom way:
|
|
701
|
+
*
|
|
702
|
+
* var element = editor.createElement( 'div' );
|
|
703
|
+
* editor.insertElement( element );
|
|
704
|
+
* var widget = editor.widgets.initOn( element, 'simplebox' );
|
|
705
|
+
*
|
|
706
|
+
* @since 4.3
|
|
707
|
+
* @class CKEDITOR.plugins.widget
|
|
708
|
+
* @mixins CKEDITOR.event
|
|
709
|
+
* @extends CKEDITOR.plugins.widget.definition
|
|
710
|
+
* @constructor Creates an instance of the widget class. Do not use it directly, but instead initialize widgets
|
|
711
|
+
* by using the {@link CKEDITOR.plugins.widget.repository#initOn} method or by the upcasting system.
|
|
712
|
+
* @param {CKEDITOR.plugins.widget.repository} widgetsRepo
|
|
713
|
+
* @param {Number} id Unique ID of this widget instance.
|
|
714
|
+
* @param {CKEDITOR.dom.element} element The widget element.
|
|
715
|
+
* @param {CKEDITOR.plugins.widget.definition} widgetDef Widget's registered definition.
|
|
716
|
+
* @param [startupData] Initial widget data. This data object will overwrite the default data and
|
|
717
|
+
* the data loaded from the DOM.
|
|
718
|
+
*/
|
|
719
|
+
function Widget( widgetsRepo, id, element, widgetDef, startupData ) {
|
|
720
|
+
var editor = widgetsRepo.editor;
|
|
721
|
+
|
|
722
|
+
// Extend this widget with widgetDef-specific methods and properties.
|
|
723
|
+
CKEDITOR.tools.extend( this, widgetDef, {
|
|
724
|
+
/**
|
|
725
|
+
* The editor instance.
|
|
726
|
+
*
|
|
727
|
+
* @readonly
|
|
728
|
+
* @property {CKEDITOR.editor}
|
|
729
|
+
*/
|
|
730
|
+
editor: editor,
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* This widget's unique (per editor instance) ID.
|
|
734
|
+
*
|
|
735
|
+
* @readonly
|
|
736
|
+
* @property {Number}
|
|
737
|
+
*/
|
|
738
|
+
id: id,
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Whether this widget is an inline widget (based on an inline element unless
|
|
742
|
+
* forced otherwise by {@link CKEDITOR.plugins.widget.definition#inline}).
|
|
743
|
+
*
|
|
744
|
+
* **Note:** This option does not allow to turn a block element into an inline widget.
|
|
745
|
+
* However, it makes it possible to turn an inline element into a block widget or to
|
|
746
|
+
* force a correct type in case when automatic recognition fails.
|
|
747
|
+
*
|
|
748
|
+
* @readonly
|
|
749
|
+
* @property {Boolean}
|
|
750
|
+
*/
|
|
751
|
+
inline: element.getParent().getName() == 'span',
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* The widget element — the element on which the widget was initialized.
|
|
755
|
+
*
|
|
756
|
+
* @readonly
|
|
757
|
+
* @property {CKEDITOR.dom.element} element
|
|
758
|
+
*/
|
|
759
|
+
element: element,
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Widget's data object.
|
|
763
|
+
*
|
|
764
|
+
* The data can only be set by using the {@link #setData} method.
|
|
765
|
+
* Changes made to the data fire the {@link #event-data} event.
|
|
766
|
+
*
|
|
767
|
+
* @readonly
|
|
768
|
+
*/
|
|
769
|
+
data: CKEDITOR.tools.extend( {}, typeof widgetDef.defaults == 'function' ? widgetDef.defaults() : widgetDef.defaults ),
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Indicates if a widget is data-ready. Set to `true` when data from all sources
|
|
773
|
+
* ({@link CKEDITOR.plugins.widget.definition#defaults}, set in the
|
|
774
|
+
* {@link #init} method, loaded from the widget's element and startup data coming from the constructor)
|
|
775
|
+
* are finally loaded. This is immediately followed by the first {@link #event-data}.
|
|
776
|
+
*
|
|
777
|
+
* @readonly
|
|
778
|
+
*/
|
|
779
|
+
dataReady: false,
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Whether a widget instance was initialized. This means that:
|
|
783
|
+
*
|
|
784
|
+
* * An instance was created,
|
|
785
|
+
* * Its properties were set,
|
|
786
|
+
* * The `init` method was executed.
|
|
787
|
+
*
|
|
788
|
+
* **Note**: The first {@link #event-data} event could not be fired yet which
|
|
789
|
+
* means that the widget's DOM has not been set up yet. Wait for the {@link #event-ready}
|
|
790
|
+
* event to be notified when a widget is fully initialized and ready.
|
|
791
|
+
*
|
|
792
|
+
* **Note**: Use the {@link #isInited} method to check whether a widget is initialized and
|
|
793
|
+
* has not been destroyed.
|
|
794
|
+
*
|
|
795
|
+
* @readonly
|
|
796
|
+
*/
|
|
797
|
+
inited: false,
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Whether a widget instance is ready. This means that the widget is {@link #inited} and
|
|
801
|
+
* that its DOM was finally set up.
|
|
802
|
+
*
|
|
803
|
+
* **Note:** Use the {@link #isReady} method to check whether a widget is ready and
|
|
804
|
+
* has not been destroyed.
|
|
805
|
+
*
|
|
806
|
+
* @readonly
|
|
807
|
+
*/
|
|
808
|
+
ready: false,
|
|
809
|
+
|
|
810
|
+
// Revert what widgetDef could override (automatic #edit listener).
|
|
811
|
+
edit: Widget.prototype.edit,
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* The nested editable element which is currently focused.
|
|
815
|
+
*
|
|
816
|
+
* @readonly
|
|
817
|
+
* @property {CKEDITOR.plugins.widget.nestedEditable}
|
|
818
|
+
*/
|
|
819
|
+
focusedEditable: null,
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* The widget definition from which this instance was created.
|
|
823
|
+
*
|
|
824
|
+
* @readonly
|
|
825
|
+
* @property {CKEDITOR.plugins.widget.definition} definition
|
|
826
|
+
*/
|
|
827
|
+
definition: widgetDef,
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Link to the widget repository which created this instance.
|
|
831
|
+
*
|
|
832
|
+
* @readonly
|
|
833
|
+
* @property {CKEDITOR.plugins.widget.repository} repository
|
|
834
|
+
*/
|
|
835
|
+
repository: widgetsRepo,
|
|
836
|
+
|
|
837
|
+
draggable: widgetDef.draggable !== false,
|
|
838
|
+
|
|
839
|
+
// WAAARNING: Overwrite widgetDef's priv object, because otherwise violent unicorn's gonna visit you.
|
|
840
|
+
_: {
|
|
841
|
+
downcastFn: ( widgetDef.downcast && typeof widgetDef.downcast == 'string' ) ?
|
|
842
|
+
widgetDef.downcasts[ widgetDef.downcast ] : widgetDef.downcast
|
|
843
|
+
}
|
|
844
|
+
}, true );
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* An object of widget component elements.
|
|
848
|
+
*
|
|
849
|
+
* For every `partName => selector` pair in {@link CKEDITOR.plugins.widget.definition#parts},
|
|
850
|
+
* one `partName => element` pair is added to this object during the widget initialization.
|
|
851
|
+
*
|
|
852
|
+
* @readonly
|
|
853
|
+
* @property {Object} parts
|
|
854
|
+
*/
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* The template which will be used to create a new widget element (when the widget's command is executed).
|
|
858
|
+
* It will be populated with {@link #defaults default values}.
|
|
859
|
+
*
|
|
860
|
+
* @readonly
|
|
861
|
+
* @property {CKEDITOR.template} template
|
|
862
|
+
*/
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* The widget wrapper — a non-editable `div` or `span` element (depending on {@link #inline})
|
|
866
|
+
* which is a parent of the {@link #element} and widget compontents like the drag handler and the {@link #mask}.
|
|
867
|
+
* It is the outermost widget element.
|
|
868
|
+
*
|
|
869
|
+
* @readonly
|
|
870
|
+
* @property {CKEDITOR.dom.element} wrapper
|
|
871
|
+
*/
|
|
872
|
+
|
|
873
|
+
widgetsRepo.fire( 'instanceCreated', this );
|
|
874
|
+
|
|
875
|
+
setupWidget( this, widgetDef );
|
|
876
|
+
|
|
877
|
+
this.init && this.init();
|
|
878
|
+
|
|
879
|
+
// Finally mark widget as inited.
|
|
880
|
+
this.inited = true;
|
|
881
|
+
|
|
882
|
+
setupWidgetData( this, startupData );
|
|
883
|
+
|
|
884
|
+
// If at some point (e.g. in #data listener) widget hasn't been destroyed
|
|
885
|
+
// and widget is already attached to document then fire #ready.
|
|
886
|
+
if ( this.isInited() && editor.editable().contains( this.wrapper ) ) {
|
|
887
|
+
this.ready = true;
|
|
888
|
+
this.fire( 'ready' );
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
Widget.prototype = {
|
|
893
|
+
/**
|
|
894
|
+
* Adds a class to the widget element. This method is used by
|
|
895
|
+
* the {@link #applyStyle} method and should be overriden by widgets
|
|
896
|
+
* which should handle classes differently (e.g. add them to other elements).
|
|
897
|
+
*
|
|
898
|
+
* **Note**: This method should not be used directly. Use the {@link #setData} method to
|
|
899
|
+
* set the `classes` property. Read more in the {@link #setData} documentation.
|
|
900
|
+
*
|
|
901
|
+
* See also: {@link #removeClass}, {@link #hasClass}, {@link #getClasses}.
|
|
902
|
+
*
|
|
903
|
+
* @since 4.4
|
|
904
|
+
* @param {String} className The class name to be added.
|
|
905
|
+
*/
|
|
906
|
+
addClass: function( className ) {
|
|
907
|
+
this.element.addClass( className );
|
|
908
|
+
},
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Applies the specified style to the widget. It is highly recommended to use the
|
|
912
|
+
* {@link CKEDITOR.editor#applyStyle} or {@link CKEDITOR.style#apply} methods instead of
|
|
913
|
+
* using this method directly, because unlike editor's and style's methods, this one
|
|
914
|
+
* does not perform any checks.
|
|
915
|
+
*
|
|
916
|
+
* By default this method handles only classes defined in the style. It clones existing
|
|
917
|
+
* classes which are stored in the {@link #property-data widget data}'s `classes` property,
|
|
918
|
+
* adds new classes, and calls the {@link #setData} method if at least one new class was added.
|
|
919
|
+
* Then, using the {@link #event-data} event listener widget applies modifications passing
|
|
920
|
+
* new classes to the {@link #addClass} method.
|
|
921
|
+
*
|
|
922
|
+
* If you need to handle classes differently than in the default way, you can override the
|
|
923
|
+
* {@link #addClass} and related methods. You can also handle other style properties than `classes`
|
|
924
|
+
* by overriding this method.
|
|
925
|
+
*
|
|
926
|
+
* See also: {@link #checkStyleActive}, {@link #removeStyle}.
|
|
927
|
+
*
|
|
928
|
+
* @since 4.4
|
|
929
|
+
* @param {CKEDITOR.style} style The custom widget style to be applied.
|
|
930
|
+
*/
|
|
931
|
+
applyStyle: function( style ) {
|
|
932
|
+
applyRemoveStyle( this, style, 1 );
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Checks if the specified style is applied to this widget. It is highly recommended to use the
|
|
937
|
+
* {@link CKEDITOR.style#checkActive} method instead of using this method directly,
|
|
938
|
+
* because unlike style's method, this one does not perform any checks.
|
|
939
|
+
*
|
|
940
|
+
* By default this method handles only classes defined in the style and passes
|
|
941
|
+
* them to the {@link #hasClass} method. You can override these methods to handle classes
|
|
942
|
+
* differently or to handle more of the style properties.
|
|
943
|
+
*
|
|
944
|
+
* See also: {@link #applyStyle}, {@link #removeStyle}.
|
|
945
|
+
*
|
|
946
|
+
* @since 4.4
|
|
947
|
+
* @param {CKEDITOR.style} style The custom widget style to be checked.
|
|
948
|
+
* @returns {Boolean} Whether the style is applied to this widget.
|
|
949
|
+
*/
|
|
950
|
+
checkStyleActive: function( style ) {
|
|
951
|
+
var classes = getStyleClasses( style ),
|
|
952
|
+
cl;
|
|
953
|
+
|
|
954
|
+
if ( !classes )
|
|
955
|
+
return false;
|
|
956
|
+
|
|
957
|
+
while ( ( cl = classes.pop() ) ) {
|
|
958
|
+
if ( !this.hasClass( cl ) )
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
return true;
|
|
962
|
+
},
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Destroys this widget instance.
|
|
966
|
+
*
|
|
967
|
+
* Use {@link CKEDITOR.plugins.widget.repository#destroy} when possible instead of this method.
|
|
968
|
+
*
|
|
969
|
+
* This method fires the {#event-destroy} event.
|
|
970
|
+
*
|
|
971
|
+
* @param {Boolean} [offline] Whether a widget is offline (detached from the DOM tree) —
|
|
972
|
+
* in this case the DOM (attributes, classes, etc.) will not be cleaned up.
|
|
973
|
+
*/
|
|
974
|
+
destroy: function( offline ) {
|
|
975
|
+
var editor = this.editor;
|
|
976
|
+
|
|
977
|
+
this.fire( 'destroy' );
|
|
978
|
+
|
|
979
|
+
if ( this.editables ) {
|
|
980
|
+
for ( var name in this.editables )
|
|
981
|
+
this.destroyEditable( name, offline );
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if ( !offline ) {
|
|
985
|
+
if ( this.element.data( 'cke-widget-keep-attr' ) == '0' )
|
|
986
|
+
this.element.removeAttribute( 'data-widget' );
|
|
987
|
+
this.element.removeAttributes( [ 'data-cke-widget-data', 'data-cke-widget-keep-attr' ] );
|
|
988
|
+
this.element.removeClass( 'cke_widget_element' );
|
|
989
|
+
this.element.replace( this.wrapper );
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
this.wrapper = null;
|
|
993
|
+
},
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Destroys a nested editable.
|
|
997
|
+
*
|
|
998
|
+
* @param {String} editableName Nested editable name.
|
|
999
|
+
* @param {Boolean} [offline] See {@link #method-destroy} method.
|
|
1000
|
+
*/
|
|
1001
|
+
destroyEditable: function( editableName, offline ) {
|
|
1002
|
+
var editable = this.editables[ editableName ];
|
|
1003
|
+
|
|
1004
|
+
editable.removeListener( 'focus', onEditableFocus );
|
|
1005
|
+
editable.removeListener( 'blur', onEditableBlur );
|
|
1006
|
+
this.editor.focusManager.remove( editable );
|
|
1007
|
+
|
|
1008
|
+
if ( !offline ) {
|
|
1009
|
+
editable.removeClass( 'cke_widget_editable' );
|
|
1010
|
+
editable.removeClass( 'cke_widget_editable_focused' );
|
|
1011
|
+
editable.removeAttributes( [ 'contenteditable', 'data-cke-widget-editable', 'data-cke-enter-mode' ] );
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
delete this.editables[ editableName ];
|
|
1015
|
+
},
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Starts widget editing.
|
|
1019
|
+
*
|
|
1020
|
+
* This method fires the {@link CKEDITOR.plugins.widget#event-edit} event
|
|
1021
|
+
* which may be cancelled in order to prevent it from opening a dialog window.
|
|
1022
|
+
*
|
|
1023
|
+
* The dialog window name is obtained from the event's data `dialog` property or
|
|
1024
|
+
* from {@link CKEDITOR.plugins.widget.definition#dialog}.
|
|
1025
|
+
*/
|
|
1026
|
+
edit: function() {
|
|
1027
|
+
var evtData = { dialog: this.dialog },
|
|
1028
|
+
that = this;
|
|
1029
|
+
|
|
1030
|
+
// Edit event was blocked, but there's no dialog to be automatically opened.
|
|
1031
|
+
if ( this.fire( 'edit', evtData ) === false || !evtData.dialog )
|
|
1032
|
+
return;
|
|
1033
|
+
|
|
1034
|
+
this.editor.openDialog( evtData.dialog, function( dialog ) {
|
|
1035
|
+
var showListener,
|
|
1036
|
+
okListener;
|
|
1037
|
+
|
|
1038
|
+
// Allow to add a custom dialog handler.
|
|
1039
|
+
if ( that.fire( 'dialog', dialog ) === false )
|
|
1040
|
+
return;
|
|
1041
|
+
|
|
1042
|
+
showListener = dialog.on( 'show', function() {
|
|
1043
|
+
dialog.setupContent( that );
|
|
1044
|
+
} );
|
|
1045
|
+
|
|
1046
|
+
okListener = dialog.on( 'ok', function() {
|
|
1047
|
+
// Commit dialog's fields, but prevent from
|
|
1048
|
+
// firing data event for every field. Fire only one,
|
|
1049
|
+
// bulk event at the end.
|
|
1050
|
+
var dataChanged,
|
|
1051
|
+
dataListener = that.on( 'data', function( evt ) {
|
|
1052
|
+
dataChanged = 1;
|
|
1053
|
+
evt.cancel();
|
|
1054
|
+
}, null, null, 0 );
|
|
1055
|
+
|
|
1056
|
+
// Create snapshot preceeding snapshot with changed widget...
|
|
1057
|
+
// TODO it should not be required, but it is and I found similar
|
|
1058
|
+
// code in dialog#ok listener in dialog/plugin.js.
|
|
1059
|
+
that.editor.fire( 'saveSnapshot' );
|
|
1060
|
+
dialog.commitContent( that );
|
|
1061
|
+
|
|
1062
|
+
dataListener.removeListener();
|
|
1063
|
+
if ( dataChanged ) {
|
|
1064
|
+
that.fire( 'data', that.data );
|
|
1065
|
+
that.editor.fire( 'saveSnapshot' );
|
|
1066
|
+
}
|
|
1067
|
+
} );
|
|
1068
|
+
|
|
1069
|
+
dialog.once( 'hide', function() {
|
|
1070
|
+
showListener.removeListener();
|
|
1071
|
+
okListener.removeListener();
|
|
1072
|
+
} );
|
|
1073
|
+
} );
|
|
1074
|
+
},
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Returns widget element classes parsed to an object. This method
|
|
1078
|
+
* is used to populate the `classes` property of widget's {@link #property-data}.
|
|
1079
|
+
*
|
|
1080
|
+
* This method reuses {@link CKEDITOR.plugins.widget.repository#parseElementClasses}.
|
|
1081
|
+
* It should be overriden if a widget should handle classes differently (e.g. on other elements).
|
|
1082
|
+
*
|
|
1083
|
+
* See also: {@link #removeClass}, {@link #addClass}, {@link #hasClass}.
|
|
1084
|
+
*
|
|
1085
|
+
* @since 4.4
|
|
1086
|
+
* @returns {Object}
|
|
1087
|
+
*/
|
|
1088
|
+
getClasses: function() {
|
|
1089
|
+
return this.repository.parseElementClasses( this.element.getAttribute( 'class' ) );
|
|
1090
|
+
},
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Checks if the widget element has specified class. This method is used by
|
|
1094
|
+
* the {@link #checkStyleActive} method and should be overriden by widgets
|
|
1095
|
+
* which should handle classes differently (e.g. on other elements).
|
|
1096
|
+
*
|
|
1097
|
+
* See also: {@link #removeClass}, {@link #addClass}, {@link #getClasses}.
|
|
1098
|
+
*
|
|
1099
|
+
* @since 4.4
|
|
1100
|
+
* @param {String} className The class to be checked.
|
|
1101
|
+
* @param {Boolean} Whether a widget has specified class.
|
|
1102
|
+
*/
|
|
1103
|
+
hasClass: function( className ) {
|
|
1104
|
+
return this.element.hasClass( className );
|
|
1105
|
+
},
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Initializes a nested editable.
|
|
1109
|
+
*
|
|
1110
|
+
* **Note**: Only elements from {@link CKEDITOR.dtd#$editable} may become editables.
|
|
1111
|
+
*
|
|
1112
|
+
* @param {String} editableName The nested editable name.
|
|
1113
|
+
* @param {CKEDITOR.plugins.widget.nestedEditable.definition} definition The definition of the nested editable.
|
|
1114
|
+
* @returns {Boolean} Whether an editable was successfully initialized.
|
|
1115
|
+
*/
|
|
1116
|
+
initEditable: function( editableName, definition ) {
|
|
1117
|
+
var editable = this.wrapper.findOne( definition.selector );
|
|
1118
|
+
|
|
1119
|
+
if ( editable && editable.is( CKEDITOR.dtd.$editable ) ) {
|
|
1120
|
+
editable = new NestedEditable( this.editor, editable, {
|
|
1121
|
+
filter: createEditableFilter.call( this.repository, this.name, editableName, definition )
|
|
1122
|
+
} );
|
|
1123
|
+
this.editables[ editableName ] = editable;
|
|
1124
|
+
|
|
1125
|
+
editable.setAttributes( {
|
|
1126
|
+
contenteditable: 'true',
|
|
1127
|
+
'data-cke-widget-editable': editableName,
|
|
1128
|
+
'data-cke-enter-mode': editable.enterMode
|
|
1129
|
+
} );
|
|
1130
|
+
|
|
1131
|
+
if ( editable.filter )
|
|
1132
|
+
editable.data( 'cke-filter', editable.filter.id );
|
|
1133
|
+
|
|
1134
|
+
editable.addClass( 'cke_widget_editable' );
|
|
1135
|
+
// This class may be left when d&ding widget which
|
|
1136
|
+
// had focused editable. Clean this class here, not in
|
|
1137
|
+
// cleanUpWidgetElement for performance and code size reasons.
|
|
1138
|
+
editable.removeClass( 'cke_widget_editable_focused' );
|
|
1139
|
+
|
|
1140
|
+
if ( definition.pathName )
|
|
1141
|
+
editable.data( 'cke-display-name', definition.pathName );
|
|
1142
|
+
|
|
1143
|
+
this.editor.focusManager.add( editable );
|
|
1144
|
+
editable.on( 'focus', onEditableFocus, this );
|
|
1145
|
+
CKEDITOR.env.ie && editable.on( 'blur', onEditableBlur, this );
|
|
1146
|
+
|
|
1147
|
+
// Finally, process editable's data. This data wasn't processed when loading
|
|
1148
|
+
// editor's data, becuase they need to be processed separately, with its own filters and settings.
|
|
1149
|
+
editable.setData( editable.getHtml() );
|
|
1150
|
+
|
|
1151
|
+
return true;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return false;
|
|
1155
|
+
},
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Checks if a widget has already been initialized and has not been destroyed yet.
|
|
1159
|
+
*
|
|
1160
|
+
* See {@link #inited} for more details.
|
|
1161
|
+
*
|
|
1162
|
+
* @returns {Boolean}
|
|
1163
|
+
*/
|
|
1164
|
+
isInited: function() {
|
|
1165
|
+
return !!( this.wrapper && this.inited );
|
|
1166
|
+
},
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Checks if a widget is ready and has not been destroyed yet.
|
|
1170
|
+
*
|
|
1171
|
+
* See {@link #property-ready} for more details.
|
|
1172
|
+
*
|
|
1173
|
+
* @returns {Boolean}
|
|
1174
|
+
*/
|
|
1175
|
+
isReady: function() {
|
|
1176
|
+
return this.isInited() && this.ready;
|
|
1177
|
+
},
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Focuses a widget by selecting it.
|
|
1181
|
+
*/
|
|
1182
|
+
focus: function() {
|
|
1183
|
+
var sel = this.editor.getSelection();
|
|
1184
|
+
|
|
1185
|
+
// Fake the selection before focusing editor, to avoid unpreventable viewports scrolling
|
|
1186
|
+
// on Webkit/Blink/IE which is done because there's no selection or selection was somewhere else than widget.
|
|
1187
|
+
if ( sel )
|
|
1188
|
+
sel.fake( this.wrapper );
|
|
1189
|
+
|
|
1190
|
+
// Always focus editor (not only when focusManger.hasFocus is false) (because of #10483).
|
|
1191
|
+
this.editor.focus();
|
|
1192
|
+
},
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* Removes a class from the widget element. This method is used by
|
|
1196
|
+
* the {@link #removeStyle} method and should be overriden by widgets
|
|
1197
|
+
* which should handle classes differently (e.g. on other elements).
|
|
1198
|
+
*
|
|
1199
|
+
* **Note**: This method should not be used directly. Use the {@link #setData} method to
|
|
1200
|
+
* set the `classes` property. Read more in the {@link #setData} documentation.
|
|
1201
|
+
*
|
|
1202
|
+
* See also: {@link #hasClass}, {@link #addClass}.
|
|
1203
|
+
*
|
|
1204
|
+
* @since 4.4
|
|
1205
|
+
* @param {String} className The class to be removed.
|
|
1206
|
+
*/
|
|
1207
|
+
removeClass: function( className ) {
|
|
1208
|
+
this.element.removeClass( className );
|
|
1209
|
+
},
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Removes the specified style from the widget. It is highly recommended to use the
|
|
1213
|
+
* {@link CKEDITOR.editor#removeStyle} or {@link CKEDITOR.style#remove} methods instead of
|
|
1214
|
+
* using this method directly, because unlike editor's and style's methods, this one
|
|
1215
|
+
* does not perform any checks.
|
|
1216
|
+
*
|
|
1217
|
+
* Read more about how applying/removing styles works in the {@link #applyStyle} method documentation.
|
|
1218
|
+
*
|
|
1219
|
+
* See also {@link #checkStyleActive}, {@link #applyStyle}, {@link #getClasses}.
|
|
1220
|
+
*
|
|
1221
|
+
* @since 4.4
|
|
1222
|
+
* @param {CKEDITOR.style} style The custom widget style to be removed.
|
|
1223
|
+
*/
|
|
1224
|
+
removeStyle: function( style ) {
|
|
1225
|
+
applyRemoveStyle( this, style, 0 );
|
|
1226
|
+
},
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Sets widget value(s) in the {@link #property-data} object.
|
|
1230
|
+
* If the given value(s) modifies current ones, the {@link #event-data} event is fired.
|
|
1231
|
+
*
|
|
1232
|
+
* this.setData( 'align', 'left' );
|
|
1233
|
+
* this.data.align; // -> 'left'
|
|
1234
|
+
*
|
|
1235
|
+
* this.setData( { align: 'right', opened: false } );
|
|
1236
|
+
* this.data.align; // -> 'right'
|
|
1237
|
+
* this.data.opened; // -> false
|
|
1238
|
+
*
|
|
1239
|
+
* Set values are stored in {@link #element}'s attribute (`data-cke-widget-data`),
|
|
1240
|
+
* in a JSON string, therefore {@link #property-data} should contain
|
|
1241
|
+
* only serializable data.
|
|
1242
|
+
*
|
|
1243
|
+
* **Note:** A special data property, `classes`, exists. It contains an object with
|
|
1244
|
+
* classes which were returned by the {@link #getClasses} method during the widget initialization.
|
|
1245
|
+
* This property is then used by the {@link #applyStyle} and {@link #removeStyle} methods.
|
|
1246
|
+
* When it is changed (the reference to object must be changed!), the widget updates its classes by
|
|
1247
|
+
* using the {@link #addClass} and {@link #removeClass} methods.
|
|
1248
|
+
*
|
|
1249
|
+
* // Adding a new class.
|
|
1250
|
+
* var classes = CKEDITOR.tools.clone( widget.data.classes );
|
|
1251
|
+
* classes.newClass = 1;
|
|
1252
|
+
* widget.setData( 'classes', classes );
|
|
1253
|
+
*
|
|
1254
|
+
* // Removing a class.
|
|
1255
|
+
* var classes = CKEDITOR.tools.clone( widget.data.classes );
|
|
1256
|
+
* delete classes.newClass;
|
|
1257
|
+
* widget.setData( 'classes', classes );
|
|
1258
|
+
*
|
|
1259
|
+
* @param {String/Object} keyOrData
|
|
1260
|
+
* @param {Object} value
|
|
1261
|
+
* @chainable
|
|
1262
|
+
*/
|
|
1263
|
+
setData: function( key, value ) {
|
|
1264
|
+
var data = this.data,
|
|
1265
|
+
modified = 0;
|
|
1266
|
+
|
|
1267
|
+
if ( typeof key == 'string' ) {
|
|
1268
|
+
if ( data[ key ] !== value ) {
|
|
1269
|
+
data[ key ] = value;
|
|
1270
|
+
modified = 1;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
var newData = key;
|
|
1275
|
+
|
|
1276
|
+
for ( key in newData ) {
|
|
1277
|
+
if ( data[ key ] !== newData[ key ] ) {
|
|
1278
|
+
modified = 1;
|
|
1279
|
+
data[ key ] = newData[ key ];
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Block firing data event and overwriting data element before setupWidgetData is executed.
|
|
1285
|
+
if ( modified && this.dataReady ) {
|
|
1286
|
+
writeDataToElement( this );
|
|
1287
|
+
this.fire( 'data', data );
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
return this;
|
|
1291
|
+
},
|
|
1292
|
+
|
|
1293
|
+
/**
|
|
1294
|
+
* Changes the widget's focus state. This method is executed automatically after
|
|
1295
|
+
* a widget has been focused by the {@link #method-focus} method or a selection was moved
|
|
1296
|
+
* out of the widget.
|
|
1297
|
+
*
|
|
1298
|
+
* @param {Boolean} selected Whether to select or deselect this widget.
|
|
1299
|
+
* @chainable
|
|
1300
|
+
*/
|
|
1301
|
+
setFocused: function( focused ) {
|
|
1302
|
+
this.wrapper[ focused ? 'addClass' : 'removeClass' ]( 'cke_widget_focused' );
|
|
1303
|
+
this.fire( focused ? 'focus' : 'blur' );
|
|
1304
|
+
return this;
|
|
1305
|
+
},
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* Changes the widget's select state. This method is executed automatically after
|
|
1309
|
+
* a widget has been selected by the {@link #method-focus} method or the selection
|
|
1310
|
+
* was moved out of widget.
|
|
1311
|
+
*
|
|
1312
|
+
* @param {Boolean} selected Whether to select or deselect this widget.
|
|
1313
|
+
* @chainable
|
|
1314
|
+
*/
|
|
1315
|
+
setSelected: function( selected ) {
|
|
1316
|
+
this.wrapper[ selected ? 'addClass' : 'removeClass' ]( 'cke_widget_selected' );
|
|
1317
|
+
this.fire( selected ? 'select' : 'deselect' );
|
|
1318
|
+
return this;
|
|
1319
|
+
},
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* Repositions drag handler according to the widget's element position. Should be called from events, like mouseover.
|
|
1323
|
+
*/
|
|
1324
|
+
updateDragHandlerPosition: function() {
|
|
1325
|
+
var editor = this.editor,
|
|
1326
|
+
domElement = this.element.$,
|
|
1327
|
+
oldPos = this._.dragHandlerOffset,
|
|
1328
|
+
newPos = {
|
|
1329
|
+
x: domElement.offsetLeft,
|
|
1330
|
+
y: domElement.offsetTop - DRAG_HANDLER_SIZE
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1333
|
+
if ( oldPos && newPos.x == oldPos.x && newPos.y == oldPos.y )
|
|
1334
|
+
return;
|
|
1335
|
+
|
|
1336
|
+
// We need to make sure that dirty state is not changed (#11487).
|
|
1337
|
+
var initialDirty = editor.checkDirty();
|
|
1338
|
+
|
|
1339
|
+
editor.fire( 'lockSnapshot' );
|
|
1340
|
+
this.dragHandlerContainer.setStyles( {
|
|
1341
|
+
top: newPos.y + 'px',
|
|
1342
|
+
left: newPos.x + 'px'
|
|
1343
|
+
} );
|
|
1344
|
+
editor.fire( 'unlockSnapshot' );
|
|
1345
|
+
!initialDirty && editor.resetDirty();
|
|
1346
|
+
|
|
1347
|
+
this._.dragHandlerOffset = newPos;
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1351
|
+
CKEDITOR.event.implementOn( Widget.prototype );
|
|
1352
|
+
|
|
1353
|
+
/**
|
|
1354
|
+
* An event fired when a widget is ready (fully initialized). This event is fired after:
|
|
1355
|
+
*
|
|
1356
|
+
* * {@link #init} is called,
|
|
1357
|
+
* * The first {@link #event-data} event is fired,
|
|
1358
|
+
* * A widget is attached to the document.
|
|
1359
|
+
*
|
|
1360
|
+
* Therefore, in case of widget creation with a command which opens a dialog window, this event
|
|
1361
|
+
* will be delayed after the dialog window is closed and the widget is finally inserted into the document.
|
|
1362
|
+
*
|
|
1363
|
+
* **Note**: If your widget does not use automatic dialog window binding (i.e. you open the dialog window manually)
|
|
1364
|
+
* or another situation in which the widget wrapper is not attached to document at the time when it is
|
|
1365
|
+
* initialized occurs, you need to take care of firing {@link #event-ready} yourself.
|
|
1366
|
+
*
|
|
1367
|
+
* See also {@link #property-ready} and {@link #property-inited} properties, and
|
|
1368
|
+
* {@link #isReady} and {@link #isInited} methods.
|
|
1369
|
+
*
|
|
1370
|
+
* @event ready
|
|
1371
|
+
*/
|
|
1372
|
+
|
|
1373
|
+
/**
|
|
1374
|
+
* An event fired when a widget is about to be destroyed, but before it is
|
|
1375
|
+
* fully torn down.
|
|
1376
|
+
*
|
|
1377
|
+
* @event destroy
|
|
1378
|
+
*/
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* An event fired when a widget is focused.
|
|
1382
|
+
*
|
|
1383
|
+
* Widget can be focused by executing {@link #method-focus}.
|
|
1384
|
+
*
|
|
1385
|
+
* @event focus
|
|
1386
|
+
*/
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* An event fired when a widget is blurred.
|
|
1390
|
+
*
|
|
1391
|
+
* @event blur
|
|
1392
|
+
*/
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* An event fired when a widget is selected.
|
|
1396
|
+
*
|
|
1397
|
+
* @event select
|
|
1398
|
+
*/
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* An event fired when a widget is deselected.
|
|
1402
|
+
*
|
|
1403
|
+
* @event deselect
|
|
1404
|
+
*/
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* An event fired by the {@link #method-edit} method. It can be canceled
|
|
1408
|
+
* in order to stop the default action (opening a dialog window and/or
|
|
1409
|
+
* {@link CKEDITOR.plugins.widget.repository#finalizeCreation finalizing widget creation}).
|
|
1410
|
+
*
|
|
1411
|
+
* @event edit
|
|
1412
|
+
* @param data
|
|
1413
|
+
* @param {String} data.dialog Defaults to {@link CKEDITOR.plugins.widget.definition#dialog}
|
|
1414
|
+
* and can be changed or set by the listener.
|
|
1415
|
+
*/
|
|
1416
|
+
|
|
1417
|
+
/**
|
|
1418
|
+
* An event fired when a dialog window for widget editing is opened.
|
|
1419
|
+
* This event can be cancelled in order to handle the editing dialog in a custom manner.
|
|
1420
|
+
*
|
|
1421
|
+
* @event dialog
|
|
1422
|
+
* @param {CKEDITOR.dialog} data The opened dialog window instance.
|
|
1423
|
+
*/
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* An event fired when a key is pressed on a focused widget.
|
|
1427
|
+
* This event is forwarded from the {@link CKEDITOR.editor#key} event and
|
|
1428
|
+
* has the ability to block editor keystrokes if it is cancelled.
|
|
1429
|
+
*
|
|
1430
|
+
* @event key
|
|
1431
|
+
* @param data
|
|
1432
|
+
* @param {Number} data.keyCode A number representing the key code (or combination).
|
|
1433
|
+
*/
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* An event fired when a widget is double clicked.
|
|
1437
|
+
*
|
|
1438
|
+
* @event doubleclick
|
|
1439
|
+
* @param data
|
|
1440
|
+
* @param {CKEDITOR.dom.element} data.element The double clicked element.
|
|
1441
|
+
*/
|
|
1442
|
+
|
|
1443
|
+
/**
|
|
1444
|
+
* An event fired when the context menu is opened for a widget.
|
|
1445
|
+
*
|
|
1446
|
+
* @event contextMenu
|
|
1447
|
+
* @param data The object containing context menu options to be added
|
|
1448
|
+
* for this widget. See {@link CKEDITOR.plugins.contextMenu#addListener}.
|
|
1449
|
+
*/
|
|
1450
|
+
|
|
1451
|
+
/**
|
|
1452
|
+
* An event fired when the widget data changed. See the {@link #setData} method and the {@link #property-data} property.
|
|
1453
|
+
*
|
|
1454
|
+
* @event data
|
|
1455
|
+
*/
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* The wrapper class for editable elements inside widgets.
|
|
1461
|
+
*
|
|
1462
|
+
* Do not use directly. Use {@link CKEDITOR.plugins.widget.definition#editables} or
|
|
1463
|
+
* {@link CKEDITOR.plugins.widget#initEditable}.
|
|
1464
|
+
*
|
|
1465
|
+
* @class CKEDITOR.plugins.widget.nestedEditable
|
|
1466
|
+
* @extends CKEDITOR.dom.element
|
|
1467
|
+
* @constructor
|
|
1468
|
+
* @param {CKEDITOR.editor} editor
|
|
1469
|
+
* @param {CKEDITOR.dom.element} element
|
|
1470
|
+
* @param config
|
|
1471
|
+
* @param {CKEDITOR.filter} [config.filter]
|
|
1472
|
+
*/
|
|
1473
|
+
function NestedEditable( editor, element, config ) {
|
|
1474
|
+
// Call the base constructor.
|
|
1475
|
+
CKEDITOR.dom.element.call( this, element.$ );
|
|
1476
|
+
this.editor = editor;
|
|
1477
|
+
var filter = this.filter = config.filter;
|
|
1478
|
+
|
|
1479
|
+
// If blockless editable - always use BR mode.
|
|
1480
|
+
if ( !CKEDITOR.dtd[ this.getName() ].p )
|
|
1481
|
+
this.enterMode = this.shiftEnterMode = CKEDITOR.ENTER_BR;
|
|
1482
|
+
else {
|
|
1483
|
+
this.enterMode = filter ? filter.getAllowedEnterMode( editor.enterMode ) : editor.enterMode;
|
|
1484
|
+
this.shiftEnterMode = filter ? filter.getAllowedEnterMode( editor.shiftEnterMode, true ) : editor.shiftEnterMode;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
NestedEditable.prototype = CKEDITOR.tools.extend( CKEDITOR.tools.prototypedCopy( CKEDITOR.dom.element.prototype ), {
|
|
1489
|
+
/**
|
|
1490
|
+
* Sets the editable data. The data will be passed through the {@link CKEDITOR.editor#dataProcessor}
|
|
1491
|
+
* and the {@link CKEDITOR.editor#filter}. This ensures that the data was filtered and prepared to be
|
|
1492
|
+
* edited like the {@link CKEDITOR.editor#method-setData editor data}.
|
|
1493
|
+
*
|
|
1494
|
+
* @param {String} data
|
|
1495
|
+
*/
|
|
1496
|
+
setData: function( data ) {
|
|
1497
|
+
data = this.editor.dataProcessor.toHtml( data, {
|
|
1498
|
+
context: this.getName(),
|
|
1499
|
+
filter: this.filter,
|
|
1500
|
+
enterMode: this.enterMode
|
|
1501
|
+
} );
|
|
1502
|
+
this.setHtml( data );
|
|
1503
|
+
},
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* Gets the editable data. Like {@link #setData}, this method will process and filter the data.
|
|
1507
|
+
*
|
|
1508
|
+
* @returns {String}
|
|
1509
|
+
*/
|
|
1510
|
+
getData: function() {
|
|
1511
|
+
return this.editor.dataProcessor.toDataFormat( this.getHtml(), {
|
|
1512
|
+
context: this.getName(),
|
|
1513
|
+
filter: this.filter,
|
|
1514
|
+
enterMode: this.enterMode
|
|
1515
|
+
} );
|
|
1516
|
+
}
|
|
1517
|
+
} );
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* The editor instance.
|
|
1521
|
+
*
|
|
1522
|
+
* @readonly
|
|
1523
|
+
* @property {CKEDITOR.editor} editor
|
|
1524
|
+
*/
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* The filter instance if allowed content rules were defined.
|
|
1528
|
+
*
|
|
1529
|
+
* @readonly
|
|
1530
|
+
* @property {CKEDITOR.filter} filter
|
|
1531
|
+
*/
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* The enter mode active in this editable.
|
|
1535
|
+
* It is determined from editable's name (whether it is a blockless editable),
|
|
1536
|
+
* its allowed content rules (if defined) and the default editor's mode.
|
|
1537
|
+
*
|
|
1538
|
+
* @readonly
|
|
1539
|
+
* @property {Number} enterMode
|
|
1540
|
+
*/
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* The shift enter move active in this editable.
|
|
1544
|
+
*
|
|
1545
|
+
* @readonly
|
|
1546
|
+
* @property {Number} shiftEnterMode
|
|
1547
|
+
*/
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
//
|
|
1551
|
+
// REPOSITORY helpers -----------------------------------------------------
|
|
1552
|
+
//
|
|
1553
|
+
|
|
1554
|
+
function addWidgetButtons( editor ) {
|
|
1555
|
+
var widgets = editor.widgets.registered,
|
|
1556
|
+
widget,
|
|
1557
|
+
widgetName,
|
|
1558
|
+
widgetButton;
|
|
1559
|
+
|
|
1560
|
+
for ( widgetName in widgets ) {
|
|
1561
|
+
widget = widgets[ widgetName ];
|
|
1562
|
+
|
|
1563
|
+
// Create button if defined.
|
|
1564
|
+
widgetButton = widget.button;
|
|
1565
|
+
if ( widgetButton && editor.ui.addButton ) {
|
|
1566
|
+
editor.ui.addButton( CKEDITOR.tools.capitalize( widget.name, true ), {
|
|
1567
|
+
label: widgetButton,
|
|
1568
|
+
command: widget.name,
|
|
1569
|
+
toolbar: 'insert,10'
|
|
1570
|
+
} );
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// Create a command creating and editing widget.
|
|
1576
|
+
//
|
|
1577
|
+
// @param editor
|
|
1578
|
+
// @param {CKEDITOR.plugins.widget.definition} widgetDef
|
|
1579
|
+
function addWidgetCommand( editor, widgetDef ) {
|
|
1580
|
+
editor.addCommand( widgetDef.name, {
|
|
1581
|
+
exec: function() {
|
|
1582
|
+
var focused = editor.widgets.focused;
|
|
1583
|
+
// If a widget of the same type is focused, start editing.
|
|
1584
|
+
if ( focused && focused.name == widgetDef.name )
|
|
1585
|
+
focused.edit();
|
|
1586
|
+
// Otherwise...
|
|
1587
|
+
// ... use insert method is was defined.
|
|
1588
|
+
else if ( widgetDef.insert )
|
|
1589
|
+
widgetDef.insert();
|
|
1590
|
+
// ... or create a brand-new widget from template.
|
|
1591
|
+
else if ( widgetDef.template ) {
|
|
1592
|
+
var defaults = typeof widgetDef.defaults == 'function' ? widgetDef.defaults() : widgetDef.defaults,
|
|
1593
|
+
element = CKEDITOR.dom.element.createFromHtml( widgetDef.template.output( defaults ) ),
|
|
1594
|
+
instance,
|
|
1595
|
+
wrapper = editor.widgets.wrapElement( element, widgetDef.name ),
|
|
1596
|
+
temp = new CKEDITOR.dom.documentFragment( wrapper.getDocument() );
|
|
1597
|
+
|
|
1598
|
+
// Append wrapper to a temporary document. This will unify the environment
|
|
1599
|
+
// in which #data listeners work when creating and editing widget.
|
|
1600
|
+
temp.append( wrapper );
|
|
1601
|
+
instance = editor.widgets.initOn( element, widgetDef );
|
|
1602
|
+
|
|
1603
|
+
// Instance could be destroyed during initialization.
|
|
1604
|
+
// In this case finalize creation if some new widget
|
|
1605
|
+
// was left in temporary document fragment.
|
|
1606
|
+
if ( !instance ) {
|
|
1607
|
+
finalizeCreation();
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// Listen on edit to finalize widget insertion.
|
|
1612
|
+
//
|
|
1613
|
+
// * If dialog was set, then insert widget after dialog was successfully saved or destroy this
|
|
1614
|
+
// temporary instance.
|
|
1615
|
+
// * If dialog wasn't set and edit wasn't canceled, insert widget.
|
|
1616
|
+
var editListener = instance.once( 'edit', function( evt ) {
|
|
1617
|
+
if ( evt.data.dialog ) {
|
|
1618
|
+
instance.once( 'dialog', function( evt ) {
|
|
1619
|
+
var dialog = evt.data,
|
|
1620
|
+
okListener,
|
|
1621
|
+
cancelListener;
|
|
1622
|
+
|
|
1623
|
+
// Finalize creation AFTER (20) new data was set.
|
|
1624
|
+
okListener = dialog.once( 'ok', finalizeCreation, null, null, 20 );
|
|
1625
|
+
|
|
1626
|
+
cancelListener = dialog.once( 'cancel', function() {
|
|
1627
|
+
editor.widgets.destroy( instance, true );
|
|
1628
|
+
} );
|
|
1629
|
+
|
|
1630
|
+
dialog.once( 'hide', function() {
|
|
1631
|
+
okListener.removeListener();
|
|
1632
|
+
cancelListener.removeListener();
|
|
1633
|
+
} );
|
|
1634
|
+
} );
|
|
1635
|
+
}
|
|
1636
|
+
// Dialog hasn't been set, so insert widget now.
|
|
1637
|
+
else
|
|
1638
|
+
finalizeCreation();
|
|
1639
|
+
}, null, null, 999 );
|
|
1640
|
+
|
|
1641
|
+
instance.edit();
|
|
1642
|
+
|
|
1643
|
+
// Remove listener in case someone canceled it before this
|
|
1644
|
+
// listener was executed.
|
|
1645
|
+
editListener.removeListener();
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
function finalizeCreation() {
|
|
1649
|
+
editor.widgets.finalizeCreation( temp );
|
|
1650
|
+
}
|
|
1651
|
+
},
|
|
1652
|
+
|
|
1653
|
+
refresh: function( editor, path ) {
|
|
1654
|
+
// Disable widgets' commands inside nested editables -
|
|
1655
|
+
// check if blockLimit is a nested editable or a descendant of any.
|
|
1656
|
+
this.setState( getNestedEditable( editor.editable(), path.blockLimit ) ? CKEDITOR.TRISTATE_DISABLED : CKEDITOR.TRISTATE_OFF );
|
|
1657
|
+
},
|
|
1658
|
+
// A hack to force command refreshing on context change.
|
|
1659
|
+
context: 'div',
|
|
1660
|
+
|
|
1661
|
+
allowedContent: widgetDef.allowedContent,
|
|
1662
|
+
requiredContent: widgetDef.requiredContent,
|
|
1663
|
+
contentForms: widgetDef.contentForms,
|
|
1664
|
+
contentTransformations: widgetDef.contentTransformations
|
|
1665
|
+
} );
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
function addWidgetProcessors( widgetsRepo, widgetDef ) {
|
|
1669
|
+
var upcast = widgetDef.upcast,
|
|
1670
|
+
upcasts;
|
|
1671
|
+
|
|
1672
|
+
if ( !upcast )
|
|
1673
|
+
return;
|
|
1674
|
+
|
|
1675
|
+
// Multiple upcasts defined in string.
|
|
1676
|
+
if ( typeof upcast == 'string' ) {
|
|
1677
|
+
upcasts = upcast.split( ',' );
|
|
1678
|
+
while ( upcasts.length )
|
|
1679
|
+
widgetsRepo._.upcasts.push( [ widgetDef.upcasts[ upcasts.pop() ], widgetDef.name ] );
|
|
1680
|
+
}
|
|
1681
|
+
// Single rule which is automatically activated.
|
|
1682
|
+
else
|
|
1683
|
+
widgetsRepo._.upcasts.push( [ upcast, widgetDef.name ] );
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
function blurWidget( widgetsRepo, widget ) {
|
|
1687
|
+
widgetsRepo.focused = null;
|
|
1688
|
+
|
|
1689
|
+
if ( widget.isInited() ) {
|
|
1690
|
+
// Widget could be destroyed in the meantime - e.g. data could be set.
|
|
1691
|
+
widgetsRepo.fire( 'widgetBlurred', { widget: widget } );
|
|
1692
|
+
widget.setFocused( false );
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
function checkWidgets( evt ) {
|
|
1697
|
+
var options = evt.data;
|
|
1698
|
+
|
|
1699
|
+
if ( this.editor.mode != 'wysiwyg' )
|
|
1700
|
+
return;
|
|
1701
|
+
|
|
1702
|
+
var editable = this.editor.editable(),
|
|
1703
|
+
instances = this.instances,
|
|
1704
|
+
newInstances, i, count, wrapper;
|
|
1705
|
+
|
|
1706
|
+
if ( !editable )
|
|
1707
|
+
return;
|
|
1708
|
+
|
|
1709
|
+
// Remove widgets which have no corresponding elements in DOM.
|
|
1710
|
+
for ( i in instances ) {
|
|
1711
|
+
if ( !editable.contains( instances[ i ].wrapper ) )
|
|
1712
|
+
this.destroy( instances[ i ], true );
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Init on all (new) if initOnlyNew option was passed.
|
|
1716
|
+
if ( options && options.initOnlyNew )
|
|
1717
|
+
newInstances = this.initOnAll();
|
|
1718
|
+
else {
|
|
1719
|
+
var wrappers = editable.find( '.cke_widget_wrapper' );
|
|
1720
|
+
newInstances = [];
|
|
1721
|
+
|
|
1722
|
+
// Create widgets on existing wrappers if they do not exists.
|
|
1723
|
+
for ( i = 0, count = wrappers.count(); i < count; i++ ) {
|
|
1724
|
+
wrapper = wrappers.getItem( i );
|
|
1725
|
+
|
|
1726
|
+
// Check if there's no instance for this widget and that
|
|
1727
|
+
// wrapper is not inside some temporary element like copybin (#11088).
|
|
1728
|
+
if ( !this.getByElement( wrapper, true ) && !findParent( wrapper, isDomTemp ) ) {
|
|
1729
|
+
// Add cke_widget_new class because otherwise
|
|
1730
|
+
// widget will not be created on such wrapper.
|
|
1731
|
+
wrapper.addClass( 'cke_widget_new' );
|
|
1732
|
+
newInstances.push( this.initOn( wrapper.getFirst( isDomWidgetElement ) ) );
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// If only single widget was initialized and focusInited was passed, focus it.
|
|
1738
|
+
if ( options && options.focusInited && newInstances.length == 1 )
|
|
1739
|
+
newInstances[ 0 ].focus();
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// Unwraps widget element and clean up element.
|
|
1743
|
+
//
|
|
1744
|
+
// This function is used to clean up pasted widgets.
|
|
1745
|
+
// It should have similar result to widget#destroy plus
|
|
1746
|
+
// some additional adjustments, specific for pasting.
|
|
1747
|
+
//
|
|
1748
|
+
// @param {CKEDITOR.htmlParser.element} el
|
|
1749
|
+
function cleanUpWidgetElement( el ) {
|
|
1750
|
+
var parent = el.parent;
|
|
1751
|
+
if ( parent.type == CKEDITOR.NODE_ELEMENT && parent.attributes[ 'data-cke-widget-wrapper' ] )
|
|
1752
|
+
parent.replaceWith( el );
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// Similar to cleanUpWidgetElement, but works on DOM and finds
|
|
1756
|
+
// widget elements by its own.
|
|
1757
|
+
//
|
|
1758
|
+
// Unlike cleanUpWidgetElement it will wrap element back.
|
|
1759
|
+
//
|
|
1760
|
+
// @param {CKEDITOR.dom.element} container
|
|
1761
|
+
function cleanUpAllWidgetElements( widgetsRepo, container ) {
|
|
1762
|
+
var wrappers = container.find( '.cke_widget_wrapper' ),
|
|
1763
|
+
wrapper, element,
|
|
1764
|
+
i = 0,
|
|
1765
|
+
l = wrappers.count();
|
|
1766
|
+
|
|
1767
|
+
for ( ; i < l; ++i ) {
|
|
1768
|
+
wrapper = wrappers.getItem( i );
|
|
1769
|
+
element = wrapper.getFirst( isDomWidgetElement );
|
|
1770
|
+
// If wrapper contains widget element - unwrap it and wrap again.
|
|
1771
|
+
if ( element.type == CKEDITOR.NODE_ELEMENT && element.data( 'widget' ) ) {
|
|
1772
|
+
element.replace( wrapper );
|
|
1773
|
+
widgetsRepo.wrapElement( element );
|
|
1774
|
+
}
|
|
1775
|
+
// Otherwise - something is wrong... clean this up.
|
|
1776
|
+
else
|
|
1777
|
+
wrapper.remove();
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// Creates {@link CKEDITOR.filter} instance for given widget, editable and rules.
|
|
1782
|
+
//
|
|
1783
|
+
// Once filter for widget-editable pair is created it is cached, so the same instance
|
|
1784
|
+
// will be returned when method is executed again.
|
|
1785
|
+
//
|
|
1786
|
+
// @param {String} widgetName
|
|
1787
|
+
// @param {String} editableName
|
|
1788
|
+
// @param {CKEDITOR.plugins.widget.nestedEditableDefinition} editableDefinition The nested editable definition.
|
|
1789
|
+
// @returns {CKEDITOR.filter} Filter instance or `null` if rules are not defined.
|
|
1790
|
+
// @context CKEDITOR.plugins.widget.repository
|
|
1791
|
+
function createEditableFilter( widgetName, editableName, editableDefinition ) {
|
|
1792
|
+
if ( !editableDefinition.allowedContent )
|
|
1793
|
+
return null;
|
|
1794
|
+
|
|
1795
|
+
var editables = this._.filters[ widgetName ];
|
|
1796
|
+
|
|
1797
|
+
if ( !editables )
|
|
1798
|
+
this._.filters[ widgetName ] = editables = {};
|
|
1799
|
+
|
|
1800
|
+
var filter = editables[ editableName ];
|
|
1801
|
+
|
|
1802
|
+
if ( !filter )
|
|
1803
|
+
editables[ editableName ] = filter = new CKEDITOR.filter( editableDefinition.allowedContent );
|
|
1804
|
+
|
|
1805
|
+
return filter;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// Creates an iterator function which when executed on all
|
|
1809
|
+
// elements in DOM tree will gather elements that should be wrapped
|
|
1810
|
+
// and initialized as widgets.
|
|
1811
|
+
function createUpcastIterator( widgetsRepo ) {
|
|
1812
|
+
var toBeWrapped = [],
|
|
1813
|
+
upcasts = widgetsRepo._.upcasts,
|
|
1814
|
+
upcastCallbacks = widgetsRepo._.upcastCallbacks;
|
|
1815
|
+
|
|
1816
|
+
return {
|
|
1817
|
+
toBeWrapped: toBeWrapped,
|
|
1818
|
+
|
|
1819
|
+
iterator: function( element ) {
|
|
1820
|
+
var upcast, upcasted,
|
|
1821
|
+
data,
|
|
1822
|
+
i,
|
|
1823
|
+
upcastsLength,
|
|
1824
|
+
upcastCallbacksLength;
|
|
1825
|
+
|
|
1826
|
+
// Wrapper found - find widget element, add it to be
|
|
1827
|
+
// cleaned up (unwrapped) and wrapped and stop iterating in this branch.
|
|
1828
|
+
if ( 'data-cke-widget-wrapper' in element.attributes ) {
|
|
1829
|
+
element = element.getFirst( isParserWidgetElement );
|
|
1830
|
+
|
|
1831
|
+
if ( element )
|
|
1832
|
+
toBeWrapped.push( [ element ] );
|
|
1833
|
+
|
|
1834
|
+
// Do not iterate over descendants.
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1837
|
+
// Widget element found - add it to be cleaned up (just in case)
|
|
1838
|
+
// and wrapped and stop iterating in this branch.
|
|
1839
|
+
else if ( 'data-widget' in element.attributes ) {
|
|
1840
|
+
toBeWrapped.push( [ element ] );
|
|
1841
|
+
|
|
1842
|
+
// Do not iterate over descendants.
|
|
1843
|
+
return false;
|
|
1844
|
+
}
|
|
1845
|
+
else if ( ( upcastsLength = upcasts.length ) ) {
|
|
1846
|
+
// Ignore elements with data-cke-widget-upcasted to avoid multiple upcasts (#11533).
|
|
1847
|
+
// Do not iterate over descendants.
|
|
1848
|
+
if ( element.attributes[ 'data-cke-widget-upcasted' ] )
|
|
1849
|
+
return false;
|
|
1850
|
+
|
|
1851
|
+
// Check element with upcast callbacks first.
|
|
1852
|
+
// If any of them return false abort upcasting.
|
|
1853
|
+
for ( i = 0, upcastCallbacksLength = upcastCallbacks.length; i < upcastCallbacksLength; ++i ) {
|
|
1854
|
+
if ( upcastCallbacks[ i ]( element ) === false )
|
|
1855
|
+
return;
|
|
1856
|
+
// Return nothing in order to continue iterating over ascendants.
|
|
1857
|
+
// See http://dev.ckeditor.com/ticket/11186#comment:6
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
for ( i = 0; i < upcastsLength; ++i ) {
|
|
1861
|
+
upcast = upcasts[ i ];
|
|
1862
|
+
data = {};
|
|
1863
|
+
|
|
1864
|
+
if ( ( upcasted = upcast[ 0 ]( element, data ) ) ) {
|
|
1865
|
+
// If upcast function returned element, upcast this one.
|
|
1866
|
+
// It can be e.g. a new element wrapping the original one.
|
|
1867
|
+
if ( upcasted instanceof CKEDITOR.htmlParser.element )
|
|
1868
|
+
element = upcasted;
|
|
1869
|
+
|
|
1870
|
+
// Set initial data attr with data from upcast method.
|
|
1871
|
+
element.attributes[ 'data-cke-widget-data' ] = encodeURIComponent( JSON.stringify( data ) );
|
|
1872
|
+
element.attributes[ 'data-cke-widget-upcasted' ] = 1;
|
|
1873
|
+
|
|
1874
|
+
toBeWrapped.push( [ element, upcast[ 1 ] ] );
|
|
1875
|
+
|
|
1876
|
+
// Do not iterate over descendants.
|
|
1877
|
+
return false;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Finds a first parent that matches query.
|
|
1886
|
+
//
|
|
1887
|
+
// @param {CKEDITOR.dom.element} element
|
|
1888
|
+
// @param {Function} query
|
|
1889
|
+
function findParent( element, query ) {
|
|
1890
|
+
var parent = element;
|
|
1891
|
+
|
|
1892
|
+
while ( ( parent = parent.getParent() ) ) {
|
|
1893
|
+
if ( query( parent ) )
|
|
1894
|
+
return true;
|
|
1895
|
+
}
|
|
1896
|
+
return false;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// Gets nested editable if node is its descendant or the editable itself.
|
|
1900
|
+
//
|
|
1901
|
+
// @param {CKEDITOR.dom.element} guard Stop ancestor search on this node (usually editor's editable).
|
|
1902
|
+
// @param {CKEDITOR.dom.node} node Start search from this node.
|
|
1903
|
+
// @returns {CKEDITOR.dom.element} Element or null.
|
|
1904
|
+
function getNestedEditable( guard, node ) {
|
|
1905
|
+
if ( !node || node.equals( guard ) )
|
|
1906
|
+
return null;
|
|
1907
|
+
|
|
1908
|
+
if ( isDomNestedEditable( node ) )
|
|
1909
|
+
return node;
|
|
1910
|
+
|
|
1911
|
+
return getNestedEditable( guard, node.getParent() );
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
function getWrapperAttributes( inlineWidget ) {
|
|
1915
|
+
return {
|
|
1916
|
+
// tabindex="-1" means that it can receive focus by code.
|
|
1917
|
+
tabindex: -1,
|
|
1918
|
+
contenteditable: 'false',
|
|
1919
|
+
'data-cke-widget-wrapper': 1,
|
|
1920
|
+
'data-cke-filter': 'off',
|
|
1921
|
+
// Class cke_widget_new marks widgets which haven't been initialized yet.
|
|
1922
|
+
'class': 'cke_widget_wrapper cke_widget_new cke_widget_' +
|
|
1923
|
+
( inlineWidget ? 'inline' : 'block' )
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// Inserts element at given index.
|
|
1928
|
+
// It will check DTD and split ancestor elements up to the first
|
|
1929
|
+
// that can contain this element.
|
|
1930
|
+
//
|
|
1931
|
+
// @param {CKEDITOR.htmlParser.element} parent
|
|
1932
|
+
// @param {Number} index
|
|
1933
|
+
// @param {CKEDITOR.htmlParser.element} element
|
|
1934
|
+
function insertElement( parent, index, element ) {
|
|
1935
|
+
// Do not split doc fragment...
|
|
1936
|
+
if ( parent.type == CKEDITOR.NODE_ELEMENT ) {
|
|
1937
|
+
var parentAllows = CKEDITOR.dtd[ parent.name ];
|
|
1938
|
+
// Parent element is known (included in DTD) and cannot contain
|
|
1939
|
+
// this element.
|
|
1940
|
+
if ( parentAllows && !parentAllows[ element.name ] ) {
|
|
1941
|
+
var parent2 = parent.split( index ),
|
|
1942
|
+
parentParent = parent.parent;
|
|
1943
|
+
|
|
1944
|
+
// Element will now be inserted at right parent's index.
|
|
1945
|
+
index = parent2.getIndex();
|
|
1946
|
+
|
|
1947
|
+
// If left part of split is empty - remove it.
|
|
1948
|
+
if ( !parent.children.length ) {
|
|
1949
|
+
index -= 1;
|
|
1950
|
+
parent.remove();
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
// If right part of split is empty - remove it.
|
|
1954
|
+
if ( !parent2.children.length )
|
|
1955
|
+
parent2.remove();
|
|
1956
|
+
|
|
1957
|
+
// Try inserting as grandpas' children.
|
|
1958
|
+
return insertElement( parentParent, index, element );
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// Finally we can add this element.
|
|
1963
|
+
parent.add( element, index );
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// @param {CKEDITOR.htmlParser.element}
|
|
1967
|
+
function isParserWidgetElement( element ) {
|
|
1968
|
+
return element.type == CKEDITOR.NODE_ELEMENT && !!element.attributes[ 'data-widget' ];
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// @param {CKEDITOR.dom.element}
|
|
1972
|
+
function isDomWidgetElement( element ) {
|
|
1973
|
+
return element.type == CKEDITOR.NODE_ELEMENT && element.hasAttribute( 'data-widget' );
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// Whether for this definition and element widget should be created in inline or block mode.
|
|
1977
|
+
function isWidgetInline( widgetDef, elementName ) {
|
|
1978
|
+
return typeof widgetDef.inline == 'boolean' ? widgetDef.inline : !!CKEDITOR.dtd.$inline[ elementName ];
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// @param {CKEDITOR.htmlParser.element}
|
|
1982
|
+
function isParserWidgetWrapper( element ) {
|
|
1983
|
+
return element.type == CKEDITOR.NODE_ELEMENT && element.attributes[ 'data-cke-widget-wrapper' ];
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
// @param {CKEDITOR.dom.element}
|
|
1987
|
+
function isDomWidgetWrapper( element ) {
|
|
1988
|
+
return element.type == CKEDITOR.NODE_ELEMENT && element.hasAttribute( 'data-cke-widget-wrapper' );
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// @param {CKEDITOR.dom.element}
|
|
1992
|
+
function isDomNestedEditable( node ) {
|
|
1993
|
+
return node.type == CKEDITOR.NODE_ELEMENT && node.hasAttribute( 'data-cke-widget-editable' );
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// @param {CKEDITOR.dom.element}
|
|
1997
|
+
function isDomTemp( element ) {
|
|
1998
|
+
return element.hasAttribute( 'data-cke-temp' );
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// @param {CKEDITOR.dom.element}
|
|
2002
|
+
function isDomDragHandler( element ) {
|
|
2003
|
+
return element.type == CKEDITOR.NODE_ELEMENT && element.hasAttribute( 'data-cke-widget-drag-handler' );
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
function finalizeNativeDrop( editor, sourceWidget, range ) {
|
|
2007
|
+
// Save the snapshot with the state before moving widget.
|
|
2008
|
+
// Focus widget, so when we'll undo the DnD, widget will be focused.
|
|
2009
|
+
sourceWidget.focus();
|
|
2010
|
+
editor.fire( 'saveSnapshot' );
|
|
2011
|
+
|
|
2012
|
+
// Lock snapshot to group all steps of moving widget from the original place to the new one.
|
|
2013
|
+
editor.fire( 'lockSnapshot', { dontUpdate: true } );
|
|
2014
|
+
|
|
2015
|
+
range.select();
|
|
2016
|
+
|
|
2017
|
+
var widgetHtml = sourceWidget.wrapper.getOuterHtml();
|
|
2018
|
+
sourceWidget.wrapper.remove();
|
|
2019
|
+
editor.widgets.destroy( sourceWidget, true );
|
|
2020
|
+
editor.execCommand( 'paste', widgetHtml );
|
|
2021
|
+
|
|
2022
|
+
editor.fire( 'unlockSnapshot' );
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
function getRangeAtDropPosition( editor, dropEvt ) {
|
|
2026
|
+
var $evt = dropEvt.data.$,
|
|
2027
|
+
$range,
|
|
2028
|
+
range = editor.createRange();
|
|
2029
|
+
|
|
2030
|
+
// Make testing possible.
|
|
2031
|
+
if ( dropEvt.data.testRange )
|
|
2032
|
+
return dropEvt.data.testRange;
|
|
2033
|
+
|
|
2034
|
+
// Webkits.
|
|
2035
|
+
if ( document.caretRangeFromPoint ) {
|
|
2036
|
+
$range = editor.document.$.caretRangeFromPoint( $evt.clientX, $evt.clientY );
|
|
2037
|
+
range.setStart( CKEDITOR.dom.node( $range.startContainer ), $range.startOffset );
|
|
2038
|
+
range.collapse( true );
|
|
2039
|
+
}
|
|
2040
|
+
// FF.
|
|
2041
|
+
else if ( $evt.rangeParent ) {
|
|
2042
|
+
range.setStart( CKEDITOR.dom.node( $evt.rangeParent ), $evt.rangeOffset );
|
|
2043
|
+
range.collapse( true );
|
|
2044
|
+
}
|
|
2045
|
+
// IEs.
|
|
2046
|
+
else if ( document.body.createTextRange ) {
|
|
2047
|
+
$range = editor.document.getBody().$.createTextRange();
|
|
2048
|
+
$range.moveToPoint( $evt.clientX, $evt.clientY );
|
|
2049
|
+
var id = 'cke-temp-' + ( new Date() ).getTime();
|
|
2050
|
+
$range.pasteHTML( '<span id="' + id + '">\u200b</span>' );
|
|
2051
|
+
|
|
2052
|
+
var span = editor.document.getById( id );
|
|
2053
|
+
range.moveToPosition( span, CKEDITOR.POSITION_BEFORE_START );
|
|
2054
|
+
span.remove();
|
|
2055
|
+
}
|
|
2056
|
+
else
|
|
2057
|
+
return null;
|
|
2058
|
+
|
|
2059
|
+
return range;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
function onEditableKey( widget, keyCode ) {
|
|
2063
|
+
var focusedEditable = widget.focusedEditable,
|
|
2064
|
+
range;
|
|
2065
|
+
|
|
2066
|
+
// CTRL+A.
|
|
2067
|
+
if ( keyCode == CKEDITOR.CTRL + 65 ) {
|
|
2068
|
+
var bogus = focusedEditable.getBogus();
|
|
2069
|
+
|
|
2070
|
+
range = widget.editor.createRange();
|
|
2071
|
+
range.selectNodeContents( focusedEditable );
|
|
2072
|
+
// Exclude bogus if exists.
|
|
2073
|
+
if ( bogus )
|
|
2074
|
+
range.setEndAt( bogus, CKEDITOR.POSITION_BEFORE_START );
|
|
2075
|
+
|
|
2076
|
+
range.select();
|
|
2077
|
+
// Cancel event - block default.
|
|
2078
|
+
return false;
|
|
2079
|
+
}
|
|
2080
|
+
// DEL or BACKSPACE.
|
|
2081
|
+
else if ( keyCode == 8 || keyCode == 46 ) {
|
|
2082
|
+
var ranges = widget.editor.getSelection().getRanges();
|
|
2083
|
+
|
|
2084
|
+
range = ranges[ 0 ];
|
|
2085
|
+
|
|
2086
|
+
// Block del or backspace if at editable's boundary.
|
|
2087
|
+
return !( ranges.length == 1 && range.collapsed &&
|
|
2088
|
+
range.checkBoundaryOfElement( focusedEditable, CKEDITOR[ keyCode == 8 ? 'START' : 'END' ] ) );
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function setFocusedEditable( widgetsRepo, widget, editableElement, offline ) {
|
|
2093
|
+
var editor = widgetsRepo.editor;
|
|
2094
|
+
|
|
2095
|
+
editor.fire( 'lockSnapshot' );
|
|
2096
|
+
|
|
2097
|
+
if ( editableElement ) {
|
|
2098
|
+
var editableName = editableElement.data( 'cke-widget-editable' ),
|
|
2099
|
+
editableInstance = widget.editables[ editableName ];
|
|
2100
|
+
|
|
2101
|
+
widgetsRepo.widgetHoldingFocusedEditable = widget;
|
|
2102
|
+
widget.focusedEditable = editableInstance;
|
|
2103
|
+
editableElement.addClass( 'cke_widget_editable_focused' );
|
|
2104
|
+
|
|
2105
|
+
if ( editableInstance.filter )
|
|
2106
|
+
editor.setActiveFilter( editableInstance.filter );
|
|
2107
|
+
editor.setActiveEnterMode( editableInstance.enterMode, editableInstance.shiftEnterMode );
|
|
2108
|
+
} else {
|
|
2109
|
+
if ( !offline )
|
|
2110
|
+
widget.focusedEditable.removeClass( 'cke_widget_editable_focused' );
|
|
2111
|
+
|
|
2112
|
+
widget.focusedEditable = null;
|
|
2113
|
+
widgetsRepo.widgetHoldingFocusedEditable = null;
|
|
2114
|
+
editor.setActiveFilter( null );
|
|
2115
|
+
editor.setActiveEnterMode( null, null );
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
editor.fire( 'unlockSnapshot' );
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function setupContextMenu( editor ) {
|
|
2122
|
+
if ( !editor.contextMenu )
|
|
2123
|
+
return;
|
|
2124
|
+
|
|
2125
|
+
editor.contextMenu.addListener( function( element ) {
|
|
2126
|
+
var widget = editor.widgets.getByElement( element, true );
|
|
2127
|
+
|
|
2128
|
+
if ( widget )
|
|
2129
|
+
return widget.fire( 'contextMenu', {} );
|
|
2130
|
+
} );
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// And now we've got two problems - original problem and RegExp.
|
|
2134
|
+
// Some softeners:
|
|
2135
|
+
// * FF tends to copy all blocks up to the copybin container.
|
|
2136
|
+
// * IE tends to copy only the copybin, without its container.
|
|
2137
|
+
// * We use spans on IE and blockless editors, but divs in other cases.
|
|
2138
|
+
var pasteReplaceRegex = new RegExp(
|
|
2139
|
+
'^' +
|
|
2140
|
+
'(?:<(?:div|span)(?: data-cke-temp="1")?(?: id="cke_copybin")?(?: data-cke-temp="1")?>)?' +
|
|
2141
|
+
'(?:<(?:div|span)(?: style="[^"]+")?>)?' +
|
|
2142
|
+
'<span [^>]*data-cke-copybin-start="1"[^>]*>.?</span>([\\s\\S]+)<span [^>]*data-cke-copybin-end="1"[^>]*>.?</span>' +
|
|
2143
|
+
'(?:</(?:div|span)>)?' +
|
|
2144
|
+
'(?:</(?:div|span)>)?' +
|
|
2145
|
+
'$'
|
|
2146
|
+
);
|
|
2147
|
+
|
|
2148
|
+
function pasteReplaceFn( match, wrapperHtml ) {
|
|
2149
|
+
// Avoid polluting pasted data with any whitspaces,
|
|
2150
|
+
// what's going to break check whether only one widget was pasted.
|
|
2151
|
+
return CKEDITOR.tools.trim( wrapperHtml );
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
function setupDragAndDrop( widgetsRepo ) {
|
|
2155
|
+
var editor = widgetsRepo.editor,
|
|
2156
|
+
lineutils = CKEDITOR.plugins.lineutils;
|
|
2157
|
+
|
|
2158
|
+
editor.on( 'contentDom', function() {
|
|
2159
|
+
var editable = editor.editable(),
|
|
2160
|
+
// #11123 Firefox needs to listen on document, because otherwise event won't be fired.
|
|
2161
|
+
// #11086 IE8 cannot listen on document.
|
|
2162
|
+
dropTarget = ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) || editable.isInline() ? editable : editor.document;
|
|
2163
|
+
|
|
2164
|
+
editable.attachListener( dropTarget, 'drop', function( evt ) {
|
|
2165
|
+
var dataStr = evt.data.$.dataTransfer.getData( 'text' ),
|
|
2166
|
+
dataObj,
|
|
2167
|
+
sourceWidget,
|
|
2168
|
+
range;
|
|
2169
|
+
|
|
2170
|
+
if ( !dataStr )
|
|
2171
|
+
return;
|
|
2172
|
+
|
|
2173
|
+
try {
|
|
2174
|
+
dataObj = JSON.parse( dataStr );
|
|
2175
|
+
} catch ( e ) {
|
|
2176
|
+
// Do nothing - data couldn't be parsed so it's not a CKEditor's data.
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
if ( dataObj.type != 'cke-widget' )
|
|
2181
|
+
return;
|
|
2182
|
+
|
|
2183
|
+
evt.data.preventDefault();
|
|
2184
|
+
|
|
2185
|
+
// Something went wrong... maybe someone is dragging widgets between editors/windows/tabs/browsers/frames.
|
|
2186
|
+
if ( dataObj.editor != editor.name || !( sourceWidget = widgetsRepo.instances[ dataObj.id ] ) )
|
|
2187
|
+
return;
|
|
2188
|
+
|
|
2189
|
+
// Try to determine a DOM position at which drop happened. If none of methods
|
|
2190
|
+
// which we support succeeded abort.
|
|
2191
|
+
range = getRangeAtDropPosition( editor, evt );
|
|
2192
|
+
if ( !range )
|
|
2193
|
+
return;
|
|
2194
|
+
|
|
2195
|
+
// #11132 Hack to prevent cursor loss on Firefox. Without timeout widget is
|
|
2196
|
+
// correctly pasted but then cursor is invisible (although it works) and can be restored
|
|
2197
|
+
// only by blurring editable.
|
|
2198
|
+
if ( CKEDITOR.env.gecko )
|
|
2199
|
+
setTimeout( finalizeNativeDrop, 0, editor, sourceWidget, range );
|
|
2200
|
+
else
|
|
2201
|
+
finalizeNativeDrop( editor, sourceWidget, range );
|
|
2202
|
+
} );
|
|
2203
|
+
|
|
2204
|
+
// Register Lineutils's utilities as properties of repo.
|
|
2205
|
+
CKEDITOR.tools.extend( widgetsRepo, {
|
|
2206
|
+
finder: new lineutils.finder( editor, {
|
|
2207
|
+
lookups: {
|
|
2208
|
+
// Element is block but not list item and not in nested editable.
|
|
2209
|
+
'default': function( el ) {
|
|
2210
|
+
if ( el.is( CKEDITOR.dtd.$listItem ) )
|
|
2211
|
+
return;
|
|
2212
|
+
|
|
2213
|
+
if ( !el.is( CKEDITOR.dtd.$block ) )
|
|
2214
|
+
return;
|
|
2215
|
+
|
|
2216
|
+
while ( el ) {
|
|
2217
|
+
if ( isDomNestedEditable( el ) )
|
|
2218
|
+
return;
|
|
2219
|
+
|
|
2220
|
+
el = el.getParent();
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
return CKEDITOR.LINEUTILS_BEFORE | CKEDITOR.LINEUTILS_AFTER;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
} ),
|
|
2227
|
+
locator: new lineutils.locator( editor ),
|
|
2228
|
+
liner: new lineutils.liner( editor, {
|
|
2229
|
+
lineStyle: {
|
|
2230
|
+
cursor: 'move !important',
|
|
2231
|
+
'border-top-color': '#666'
|
|
2232
|
+
},
|
|
2233
|
+
tipLeftStyle: {
|
|
2234
|
+
'border-left-color': '#666'
|
|
2235
|
+
},
|
|
2236
|
+
tipRightStyle: {
|
|
2237
|
+
'border-right-color': '#666'
|
|
2238
|
+
}
|
|
2239
|
+
} )
|
|
2240
|
+
}, true );
|
|
2241
|
+
} );
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// Setup mouse observer which will trigger:
|
|
2245
|
+
// * widget focus on widget click,
|
|
2246
|
+
// * widget#doubleclick forwarded from editor#doubleclick.
|
|
2247
|
+
function setupMouseObserver( widgetsRepo ) {
|
|
2248
|
+
var editor = widgetsRepo.editor;
|
|
2249
|
+
|
|
2250
|
+
editor.on( 'contentDom', function() {
|
|
2251
|
+
var editable = editor.editable(),
|
|
2252
|
+
evtRoot = editable.isInline() ? editable : editor.document,
|
|
2253
|
+
widget,
|
|
2254
|
+
mouseDownOnDragHandler;
|
|
2255
|
+
|
|
2256
|
+
editable.attachListener( evtRoot, 'mousedown', function( evt ) {
|
|
2257
|
+
var target = evt.data.getTarget();
|
|
2258
|
+
|
|
2259
|
+
// #10887 Clicking scrollbar in IE8 will invoke event with empty target object.
|
|
2260
|
+
if ( !target.type )
|
|
2261
|
+
return false;
|
|
2262
|
+
|
|
2263
|
+
widget = widgetsRepo.getByElement( target );
|
|
2264
|
+
mouseDownOnDragHandler = 0; // Reset.
|
|
2265
|
+
|
|
2266
|
+
// Widget was clicked, but not editable nested in it.
|
|
2267
|
+
if ( widget ) {
|
|
2268
|
+
// Ignore mousedown on drag and drop handler if the widget is inline.
|
|
2269
|
+
// Block widgets are handled by Lineutils.
|
|
2270
|
+
if ( widget.inline && target.type == CKEDITOR.NODE_ELEMENT && target.hasAttribute( 'data-cke-widget-drag-handler' ) ) {
|
|
2271
|
+
mouseDownOnDragHandler = 1;
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
if ( !getNestedEditable( widget.wrapper, target ) ) {
|
|
2276
|
+
evt.data.preventDefault();
|
|
2277
|
+
if ( !CKEDITOR.env.ie )
|
|
2278
|
+
widget.focus();
|
|
2279
|
+
}
|
|
2280
|
+
// Reset widget so mouseup listener is not confused.
|
|
2281
|
+
else
|
|
2282
|
+
widget = null;
|
|
2283
|
+
}
|
|
2284
|
+
} );
|
|
2285
|
+
|
|
2286
|
+
// Focus widget on mouseup if mousedown was fired on drag handler.
|
|
2287
|
+
// Note: mouseup won't be fired at all if widget was dragged and dropped, so
|
|
2288
|
+
// this code will be executed only when drag handler was clicked.
|
|
2289
|
+
editable.attachListener( evtRoot, 'mouseup', function() {
|
|
2290
|
+
if ( widget && mouseDownOnDragHandler ) {
|
|
2291
|
+
mouseDownOnDragHandler = 0;
|
|
2292
|
+
widget.focus();
|
|
2293
|
+
}
|
|
2294
|
+
} );
|
|
2295
|
+
|
|
2296
|
+
// On IE it is not enough to block mousedown. If widget wrapper (element with
|
|
2297
|
+
// contenteditable=false attribute) is clicked directly (it is a target),
|
|
2298
|
+
// then after mouseup/click IE will select that element.
|
|
2299
|
+
// It is not possible to prevent that default action,
|
|
2300
|
+
// so we force fake selection after everything happened.
|
|
2301
|
+
if ( CKEDITOR.env.ie ) {
|
|
2302
|
+
editable.attachListener( evtRoot, 'mouseup', function( evt ) {
|
|
2303
|
+
if ( widget ) {
|
|
2304
|
+
setTimeout( function() {
|
|
2305
|
+
widget.focus();
|
|
2306
|
+
widget = null;
|
|
2307
|
+
} );
|
|
2308
|
+
}
|
|
2309
|
+
} );
|
|
2310
|
+
}
|
|
2311
|
+
} );
|
|
2312
|
+
|
|
2313
|
+
editor.on( 'doubleclick', function( evt ) {
|
|
2314
|
+
var widget = widgetsRepo.getByElement( evt.data.element );
|
|
2315
|
+
|
|
2316
|
+
// Not in widget or in nested editable.
|
|
2317
|
+
if ( !widget || getNestedEditable( widget.wrapper, evt.data.element ) )
|
|
2318
|
+
return;
|
|
2319
|
+
|
|
2320
|
+
return widget.fire( 'doubleclick', { element: evt.data.element } );
|
|
2321
|
+
}, null, null, 1 );
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// Setup editor#key observer which will forward it
|
|
2325
|
+
// to focused widget.
|
|
2326
|
+
function setupKeyboardObserver( widgetsRepo ) {
|
|
2327
|
+
var editor = widgetsRepo.editor;
|
|
2328
|
+
|
|
2329
|
+
editor.on( 'key', function( evt ) {
|
|
2330
|
+
var focused = widgetsRepo.focused,
|
|
2331
|
+
widgetHoldingFocusedEditable = widgetsRepo.widgetHoldingFocusedEditable,
|
|
2332
|
+
ret;
|
|
2333
|
+
|
|
2334
|
+
if ( focused )
|
|
2335
|
+
ret = focused.fire( 'key', { keyCode: evt.data.keyCode } );
|
|
2336
|
+
else if ( widgetHoldingFocusedEditable )
|
|
2337
|
+
ret = onEditableKey( widgetHoldingFocusedEditable, evt.data.keyCode );
|
|
2338
|
+
|
|
2339
|
+
return ret;
|
|
2340
|
+
}, null, null, 1 );
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
// Setup copybin on native copy and cut events in order to handle copy and cut commands
|
|
2344
|
+
// if user accepted security alert on IEs.
|
|
2345
|
+
// Note: when copying or cutting using keystroke, copySingleWidget will be first executed
|
|
2346
|
+
// by the keydown listener. Conflict between two calls will be resolved by copy_bin existence check.
|
|
2347
|
+
function setupNativeCutAndCopy( widgetsRepo ) {
|
|
2348
|
+
var editor = widgetsRepo.editor;
|
|
2349
|
+
|
|
2350
|
+
editor.on( 'contentDom', function() {
|
|
2351
|
+
var editable = editor.editable();
|
|
2352
|
+
|
|
2353
|
+
editable.attachListener( editable, 'copy', eventListener );
|
|
2354
|
+
editable.attachListener( editable, 'cut', eventListener );
|
|
2355
|
+
} );
|
|
2356
|
+
|
|
2357
|
+
function eventListener( evt ) {
|
|
2358
|
+
if ( widgetsRepo.focused )
|
|
2359
|
+
copySingleWidget( widgetsRepo.focused, evt.name == 'cut' );
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
// Setup selection observer which will trigger:
|
|
2364
|
+
// * widget select & focus on selection change,
|
|
2365
|
+
// * nested editable focus (related properites and classes) on selection change,
|
|
2366
|
+
// * deselecting and blurring all widgets on data,
|
|
2367
|
+
// * blurring widget on editor blur.
|
|
2368
|
+
function setupSelectionObserver( widgetsRepo ) {
|
|
2369
|
+
var editor = widgetsRepo.editor;
|
|
2370
|
+
|
|
2371
|
+
editor.on( 'selectionCheck', function() {
|
|
2372
|
+
widgetsRepo.fire( 'checkSelection' );
|
|
2373
|
+
} );
|
|
2374
|
+
|
|
2375
|
+
widgetsRepo.on( 'checkSelection', widgetsRepo.checkSelection, widgetsRepo );
|
|
2376
|
+
|
|
2377
|
+
editor.on( 'selectionChange', function( evt ) {
|
|
2378
|
+
var nestedEditable = getNestedEditable( editor.editable(), evt.data.selection.getStartElement() ),
|
|
2379
|
+
newWidget = nestedEditable && widgetsRepo.getByElement( nestedEditable ),
|
|
2380
|
+
oldWidget = widgetsRepo.widgetHoldingFocusedEditable;
|
|
2381
|
+
|
|
2382
|
+
if ( oldWidget ) {
|
|
2383
|
+
if ( oldWidget !== newWidget || !oldWidget.focusedEditable.equals( nestedEditable ) ) {
|
|
2384
|
+
setFocusedEditable( widgetsRepo, oldWidget, null );
|
|
2385
|
+
|
|
2386
|
+
if ( newWidget && nestedEditable )
|
|
2387
|
+
setFocusedEditable( widgetsRepo, newWidget, nestedEditable );
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
// It may happen that there's no widget even if editable was found -
|
|
2391
|
+
// e.g. if selection was automatically set in editable although widget wasn't initialized yet.
|
|
2392
|
+
else if ( newWidget && nestedEditable )
|
|
2393
|
+
setFocusedEditable( widgetsRepo, newWidget, nestedEditable );
|
|
2394
|
+
} );
|
|
2395
|
+
|
|
2396
|
+
// Invalidate old widgets early - immediately on dataReady.
|
|
2397
|
+
editor.on( 'dataReady', function( evt ) {
|
|
2398
|
+
// Deselect and blur all widgets.
|
|
2399
|
+
stateUpdater( widgetsRepo ).commit();
|
|
2400
|
+
} );
|
|
2401
|
+
|
|
2402
|
+
editor.on( 'blur', function() {
|
|
2403
|
+
var widget;
|
|
2404
|
+
|
|
2405
|
+
if ( ( widget = widgetsRepo.focused ) )
|
|
2406
|
+
blurWidget( widgetsRepo, widget );
|
|
2407
|
+
|
|
2408
|
+
if ( ( widget = widgetsRepo.widgetHoldingFocusedEditable ) )
|
|
2409
|
+
setFocusedEditable( widgetsRepo, widget, null );
|
|
2410
|
+
} );
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// Set up actions like:
|
|
2414
|
+
// * processing in toHtml/toDataFormat,
|
|
2415
|
+
// * pasting handling,
|
|
2416
|
+
// * insertion handling,
|
|
2417
|
+
// * editable reload handling (setData, mode switch, undo/redo),
|
|
2418
|
+
// * DOM invalidation handling,
|
|
2419
|
+
// * widgets checks.
|
|
2420
|
+
function setupWidgetsLifecycle( widgetsRepo ) {
|
|
2421
|
+
setupWidgetsLifecycleStart( widgetsRepo );
|
|
2422
|
+
setupWidgetsLifecycleEnd( widgetsRepo );
|
|
2423
|
+
|
|
2424
|
+
widgetsRepo.on( 'checkWidgets', checkWidgets );
|
|
2425
|
+
widgetsRepo.editor.on( 'contentDomInvalidated', widgetsRepo.checkWidgets, widgetsRepo );
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
function setupWidgetsLifecycleEnd( widgetsRepo ) {
|
|
2429
|
+
var editor = widgetsRepo.editor,
|
|
2430
|
+
downcastingSessions = {},
|
|
2431
|
+
nestedEditableScope = false;
|
|
2432
|
+
|
|
2433
|
+
// Listen before htmlDP#htmlFilter is applied to cache all widgets, because we'll
|
|
2434
|
+
// loose data-cke-* attributes.
|
|
2435
|
+
editor.on( 'toDataFormat', function( evt ) {
|
|
2436
|
+
// To avoid conflicts between htmlDP#toDF calls done at the same time
|
|
2437
|
+
// (e.g. nestedEditable#getData called during downcasting some widget)
|
|
2438
|
+
// mark every toDataFormat event chain with the downcasting session id.
|
|
2439
|
+
var id = CKEDITOR.tools.getNextNumber(),
|
|
2440
|
+
toBeDowncasted = [];
|
|
2441
|
+
evt.data.downcastingSessionId = id;
|
|
2442
|
+
downcastingSessions[ id ] = toBeDowncasted;
|
|
2443
|
+
|
|
2444
|
+
evt.data.dataValue.forEach( function( element ) {
|
|
2445
|
+
var attrs = element.attributes,
|
|
2446
|
+
widget, widgetElement;
|
|
2447
|
+
|
|
2448
|
+
// Wrapper.
|
|
2449
|
+
// Perform first part of downcasting (cleanup) and cache widgets,
|
|
2450
|
+
// because after applying DP's filter all data-cke-* attributes will be gone.
|
|
2451
|
+
if ( 'data-cke-widget-id' in attrs ) {
|
|
2452
|
+
widget = widgetsRepo.instances[ attrs[ 'data-cke-widget-id' ] ];
|
|
2453
|
+
if ( widget ) {
|
|
2454
|
+
widgetElement = element.getFirst( isParserWidgetElement );
|
|
2455
|
+
toBeDowncasted.push( {
|
|
2456
|
+
wrapper: element,
|
|
2457
|
+
element: widgetElement,
|
|
2458
|
+
widget: widget,
|
|
2459
|
+
editables: {}
|
|
2460
|
+
} );
|
|
2461
|
+
|
|
2462
|
+
// If widget did not have data-cke-widget attribute before upcasting remove it.
|
|
2463
|
+
if ( widgetElement.attributes[ 'data-cke-widget-keep-attr' ] != '1' )
|
|
2464
|
+
delete widgetElement.attributes[ 'data-widget' ];
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
// Nested editable.
|
|
2468
|
+
else if ( 'data-cke-widget-editable' in attrs ) {
|
|
2469
|
+
// Save the reference to this nested editable in the closest widget to be downcasted.
|
|
2470
|
+
// Nested editables are downcasted in the successive toDataFormat to create an opportunity
|
|
2471
|
+
// for dataFilter's "excludeNestedEditable" option to do its job (that option relies on
|
|
2472
|
+
// contenteditable="true" attribute) (#11372).
|
|
2473
|
+
toBeDowncasted[ toBeDowncasted.length - 1 ].editables[ attrs[ 'data-cke-widget-editable' ] ] = element;
|
|
2474
|
+
|
|
2475
|
+
// Don't check children - there won't be next wrapper or nested editable which we
|
|
2476
|
+
// should process in this session.
|
|
2477
|
+
return false;
|
|
2478
|
+
}
|
|
2479
|
+
}, CKEDITOR.NODE_ELEMENT, true );
|
|
2480
|
+
}, null, null, 8 );
|
|
2481
|
+
|
|
2482
|
+
// Listen after dataProcessor.htmlFilter and ACF were applied
|
|
2483
|
+
// so wrappers securing widgets' contents are removed after all filtering was done.
|
|
2484
|
+
editor.on( 'toDataFormat', function( evt ) {
|
|
2485
|
+
// Ignore some unmarked sessions.
|
|
2486
|
+
if ( !evt.data.downcastingSessionId )
|
|
2487
|
+
return;
|
|
2488
|
+
|
|
2489
|
+
var toBeDowncasted = downcastingSessions[ evt.data.downcastingSessionId ],
|
|
2490
|
+
toBe, widget, widgetElement, retElement, editableElement, e;
|
|
2491
|
+
|
|
2492
|
+
while ( ( toBe = toBeDowncasted.shift() ) ) {
|
|
2493
|
+
widget = toBe.widget;
|
|
2494
|
+
widgetElement = toBe.element;
|
|
2495
|
+
retElement = widget._.downcastFn && widget._.downcastFn.call( widget, widgetElement );
|
|
2496
|
+
|
|
2497
|
+
// Replace nested editables' content with their output data.
|
|
2498
|
+
for ( e in toBe.editables ) {
|
|
2499
|
+
editableElement = toBe.editables[ e ];
|
|
2500
|
+
|
|
2501
|
+
delete editableElement.attributes[ 'contenteditable' ];
|
|
2502
|
+
editableElement.setHtml( widget.editables[ e ].getData() );
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
// Returned element always defaults to widgetElement.
|
|
2506
|
+
if ( !retElement )
|
|
2507
|
+
retElement = widgetElement;
|
|
2508
|
+
|
|
2509
|
+
toBe.wrapper.replaceWith( retElement );
|
|
2510
|
+
}
|
|
2511
|
+
}, null, null, 13 );
|
|
2512
|
+
|
|
2513
|
+
|
|
2514
|
+
editor.on( 'contentDomUnload', function() {
|
|
2515
|
+
widgetsRepo.destroyAll( true );
|
|
2516
|
+
} );
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
function setupWidgetsLifecycleStart( widgetsRepo ) {
|
|
2520
|
+
var editor = widgetsRepo.editor,
|
|
2521
|
+
processedWidgetOnly,
|
|
2522
|
+
snapshotLoaded;
|
|
2523
|
+
|
|
2524
|
+
// Listen after ACF (so data are filtered),
|
|
2525
|
+
// but before dataProcessor.dataFilter was applied (so we can secure widgets' internals).
|
|
2526
|
+
editor.on( 'toHtml', function( evt ) {
|
|
2527
|
+
var upcastIterator = createUpcastIterator( widgetsRepo ),
|
|
2528
|
+
toBeWrapped;
|
|
2529
|
+
|
|
2530
|
+
evt.data.dataValue.forEach( upcastIterator.iterator, CKEDITOR.NODE_ELEMENT, true );
|
|
2531
|
+
|
|
2532
|
+
// Clean up and wrap all queued elements.
|
|
2533
|
+
while ( ( toBeWrapped = upcastIterator.toBeWrapped.pop() ) ) {
|
|
2534
|
+
cleanUpWidgetElement( toBeWrapped[ 0 ] );
|
|
2535
|
+
widgetsRepo.wrapElement( toBeWrapped[ 0 ], toBeWrapped[ 1 ] );
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// Used to determine whether only widget was pasted.
|
|
2539
|
+
processedWidgetOnly = evt.data.dataValue.children.length == 1 &&
|
|
2540
|
+
isParserWidgetWrapper( evt.data.dataValue.children[ 0 ] );
|
|
2541
|
+
}, null, null, 8 );
|
|
2542
|
+
|
|
2543
|
+
editor.on( 'dataReady', function() {
|
|
2544
|
+
// Clean up all widgets loaded from snapshot.
|
|
2545
|
+
if ( snapshotLoaded )
|
|
2546
|
+
cleanUpAllWidgetElements( widgetsRepo, editor.editable() );
|
|
2547
|
+
snapshotLoaded = 0;
|
|
2548
|
+
|
|
2549
|
+
// Some widgets were destroyed on contentDomUnload,
|
|
2550
|
+
// some on loadSnapshot, but that does not include
|
|
2551
|
+
// e.g. setHtml on inline editor or widgets removed just
|
|
2552
|
+
// before setting data.
|
|
2553
|
+
widgetsRepo.destroyAll( true );
|
|
2554
|
+
widgetsRepo.initOnAll();
|
|
2555
|
+
} );
|
|
2556
|
+
|
|
2557
|
+
// Set flag so dataReady will know that additional
|
|
2558
|
+
// cleanup is needed, because snapshot containing widgets was loaded.
|
|
2559
|
+
editor.on( 'loadSnapshot', function( evt ) {
|
|
2560
|
+
// Primitive but sufficient check which will prevent from executing
|
|
2561
|
+
// heavier cleanUpAllWidgetElements if not needed.
|
|
2562
|
+
if ( ( /data-cke-widget/ ).test( evt.data ) )
|
|
2563
|
+
snapshotLoaded = 1;
|
|
2564
|
+
|
|
2565
|
+
widgetsRepo.destroyAll( true );
|
|
2566
|
+
}, null, null, 9 );
|
|
2567
|
+
|
|
2568
|
+
// Handle pasted single widget.
|
|
2569
|
+
editor.on( 'paste', function( evt ) {
|
|
2570
|
+
evt.data.dataValue = evt.data.dataValue.replace( pasteReplaceRegex, pasteReplaceFn );
|
|
2571
|
+
} );
|
|
2572
|
+
|
|
2573
|
+
// Listen with high priority to check widgets after data was inserted.
|
|
2574
|
+
editor.on( 'insertText', checkNewWidgets, null, null, 999 );
|
|
2575
|
+
editor.on( 'insertHtml', checkNewWidgets, null, null, 999 );
|
|
2576
|
+
|
|
2577
|
+
function checkNewWidgets() {
|
|
2578
|
+
editor.fire( 'lockSnapshot' );
|
|
2579
|
+
|
|
2580
|
+
// Init only new for performance reason.
|
|
2581
|
+
// Focus inited if only widget was processed.
|
|
2582
|
+
widgetsRepo.checkWidgets( { initOnlyNew: true, focusInited: processedWidgetOnly } );
|
|
2583
|
+
|
|
2584
|
+
editor.fire( 'unlockSnapshot' );
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
// Helper for coordinating which widgets should be
|
|
2589
|
+
// selected/deselected and which one should be focused/blurred.
|
|
2590
|
+
function stateUpdater( widgetsRepo ) {
|
|
2591
|
+
var currentlySelected = widgetsRepo.selected,
|
|
2592
|
+
toBeSelected = [],
|
|
2593
|
+
toBeDeselected = currentlySelected.slice( 0 ),
|
|
2594
|
+
focused = null;
|
|
2595
|
+
|
|
2596
|
+
return {
|
|
2597
|
+
select: function( widget ) {
|
|
2598
|
+
if ( CKEDITOR.tools.indexOf( currentlySelected, widget ) < 0 )
|
|
2599
|
+
toBeSelected.push( widget );
|
|
2600
|
+
|
|
2601
|
+
var index = CKEDITOR.tools.indexOf( toBeDeselected, widget );
|
|
2602
|
+
if ( index >= 0 )
|
|
2603
|
+
toBeDeselected.splice( index, 1 );
|
|
2604
|
+
|
|
2605
|
+
return this;
|
|
2606
|
+
},
|
|
2607
|
+
|
|
2608
|
+
focus: function( widget ) {
|
|
2609
|
+
focused = widget;
|
|
2610
|
+
return this;
|
|
2611
|
+
},
|
|
2612
|
+
|
|
2613
|
+
commit: function() {
|
|
2614
|
+
var focusedChanged = widgetsRepo.focused !== focused,
|
|
2615
|
+
widget;
|
|
2616
|
+
|
|
2617
|
+
widgetsRepo.editor.fire( 'lockSnapshot' );
|
|
2618
|
+
|
|
2619
|
+
if ( focusedChanged && ( widget = widgetsRepo.focused ) )
|
|
2620
|
+
blurWidget( widgetsRepo, widget );
|
|
2621
|
+
|
|
2622
|
+
while ( ( widget = toBeDeselected.pop() ) ) {
|
|
2623
|
+
currentlySelected.splice( CKEDITOR.tools.indexOf( currentlySelected, widget ), 1 );
|
|
2624
|
+
// Widget could be destroyed in the meantime - e.g. data could be set.
|
|
2625
|
+
if ( widget.isInited() )
|
|
2626
|
+
widget.setSelected( false );
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
if ( focusedChanged && focused ) {
|
|
2630
|
+
widgetsRepo.focused = focused;
|
|
2631
|
+
widgetsRepo.fire( 'widgetFocused', { widget: focused } );
|
|
2632
|
+
focused.setFocused( true );
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
while ( ( widget = toBeSelected.pop() ) ) {
|
|
2636
|
+
currentlySelected.push( widget );
|
|
2637
|
+
widget.setSelected( true );
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
widgetsRepo.editor.fire( 'unlockSnapshot' );
|
|
2641
|
+
}
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
|
|
2646
|
+
//
|
|
2647
|
+
// WIDGET helpers ---------------------------------------------------------
|
|
2648
|
+
//
|
|
2649
|
+
|
|
2650
|
+
// LEFT, RIGHT, UP, DOWN, DEL, BACKSPACE - unblock default fake sel handlers.
|
|
2651
|
+
var keystrokesNotBlockedByWidget = { 37: 1, 38: 1, 39: 1, 40: 1, 8: 1, 46: 1 };
|
|
2652
|
+
|
|
2653
|
+
// Applies or removes style's classes from widget.
|
|
2654
|
+
// @param {CKEDITOR.style} style Custom widget style.
|
|
2655
|
+
// @param {Boolean} apply Whether to apply or remove style.
|
|
2656
|
+
function applyRemoveStyle( widget, style, apply ) {
|
|
2657
|
+
var changed = 0,
|
|
2658
|
+
classes = getStyleClasses( style ),
|
|
2659
|
+
updatedClasses = widget.data.classes || {},
|
|
2660
|
+
cl;
|
|
2661
|
+
|
|
2662
|
+
// Ee... Something is wrong with this style.
|
|
2663
|
+
if ( !classes )
|
|
2664
|
+
return;
|
|
2665
|
+
|
|
2666
|
+
// Clone, because we need to break reference.
|
|
2667
|
+
updatedClasses = CKEDITOR.tools.clone( updatedClasses );
|
|
2668
|
+
|
|
2669
|
+
while ( ( cl = classes.pop() ) ) {
|
|
2670
|
+
if ( apply ) {
|
|
2671
|
+
if ( !updatedClasses[ cl ] )
|
|
2672
|
+
changed = updatedClasses[ cl ] = 1;
|
|
2673
|
+
} else {
|
|
2674
|
+
if ( updatedClasses[ cl ] ) {
|
|
2675
|
+
delete updatedClasses[ cl ];
|
|
2676
|
+
changed = 1;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
if ( changed )
|
|
2681
|
+
widget.setData( 'classes', updatedClasses );
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
function cancel( evt ) {
|
|
2685
|
+
evt.cancel();
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
function copySingleWidget( widget, isCut ) {
|
|
2689
|
+
var editor = widget.editor,
|
|
2690
|
+
doc = editor.document;
|
|
2691
|
+
|
|
2692
|
+
// We're still handling previous copy/cut.
|
|
2693
|
+
// When keystroke is used to copy/cut this will also prevent
|
|
2694
|
+
// conflict with copySingleWidget called again for native copy/cut event.
|
|
2695
|
+
if ( doc.getById( 'cke_copybin' ) )
|
|
2696
|
+
return;
|
|
2697
|
+
|
|
2698
|
+
// [IE] Use span for copybin and its container to avoid bug with expanding editable height by
|
|
2699
|
+
// absolutely positioned element.
|
|
2700
|
+
var copybinName = ( editor.blockless || CKEDITOR.env.ie ) ? 'span' : 'div',
|
|
2701
|
+
copybin = doc.createElement( copybinName ),
|
|
2702
|
+
copybinContainer = doc.createElement( copybinName ),
|
|
2703
|
+
// IE8 always jumps to the end of document.
|
|
2704
|
+
needsScrollHack = CKEDITOR.env.ie && CKEDITOR.env.version < 9;
|
|
2705
|
+
|
|
2706
|
+
copybinContainer.setAttributes( {
|
|
2707
|
+
id: 'cke_copybin',
|
|
2708
|
+
'data-cke-temp': '1'
|
|
2709
|
+
} );
|
|
2710
|
+
|
|
2711
|
+
// Position copybin element outside current viewport.
|
|
2712
|
+
copybin.setStyles( {
|
|
2713
|
+
position: 'absolute',
|
|
2714
|
+
width: '1px',
|
|
2715
|
+
height: '1px',
|
|
2716
|
+
overflow: 'hidden'
|
|
2717
|
+
} );
|
|
2718
|
+
|
|
2719
|
+
copybin.setStyle( editor.config.contentsLangDirection == 'ltr' ? 'left' : 'right', '-5000px' );
|
|
2720
|
+
|
|
2721
|
+
copybin.setHtml( '<span data-cke-copybin-start="1">\u200b</span>' + widget.wrapper.getOuterHtml() + '<span data-cke-copybin-end="1">\u200b</span>' );
|
|
2722
|
+
|
|
2723
|
+
// Save snapshot with the current state.
|
|
2724
|
+
editor.fire( 'saveSnapshot' );
|
|
2725
|
+
|
|
2726
|
+
// Ignore copybin.
|
|
2727
|
+
editor.fire( 'lockSnapshot' );
|
|
2728
|
+
|
|
2729
|
+
copybinContainer.append( copybin );
|
|
2730
|
+
editor.editable().append( copybinContainer );
|
|
2731
|
+
|
|
2732
|
+
var listener1 = editor.on( 'selectionChange', cancel, null, null, 0 ),
|
|
2733
|
+
listener2 = widget.repository.on( 'checkSelection', cancel, null, null, 0 );
|
|
2734
|
+
|
|
2735
|
+
if ( needsScrollHack ) {
|
|
2736
|
+
var docElement = doc.getDocumentElement().$,
|
|
2737
|
+
scrollTop = docElement.scrollTop;
|
|
2738
|
+
}
|
|
2739
|
+
|
|
2740
|
+
// Once the clone of the widget is inside of copybin, select
|
|
2741
|
+
// the entire contents. This selection will be copied by the
|
|
2742
|
+
// native browser's clipboard system.
|
|
2743
|
+
var range = editor.createRange();
|
|
2744
|
+
range.selectNodeContents( copybin );
|
|
2745
|
+
range.select();
|
|
2746
|
+
|
|
2747
|
+
if ( needsScrollHack )
|
|
2748
|
+
docElement.scrollTop = scrollTop;
|
|
2749
|
+
|
|
2750
|
+
setTimeout( function() {
|
|
2751
|
+
// [IE] Focus widget before removing copybin to avoid scroll jump.
|
|
2752
|
+
if ( !isCut )
|
|
2753
|
+
widget.focus();
|
|
2754
|
+
|
|
2755
|
+
copybinContainer.remove();
|
|
2756
|
+
|
|
2757
|
+
listener1.removeListener();
|
|
2758
|
+
listener2.removeListener();
|
|
2759
|
+
|
|
2760
|
+
editor.fire( 'unlockSnapshot' );
|
|
2761
|
+
|
|
2762
|
+
if ( isCut ) {
|
|
2763
|
+
widget.repository.del( widget );
|
|
2764
|
+
editor.fire( 'saveSnapshot' );
|
|
2765
|
+
}
|
|
2766
|
+
}, 100 ); // Use 100ms, so Chrome (@Mac) will be able to grab the content.
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// Extracts classes array from style instance.
|
|
2770
|
+
function getStyleClasses( style ) {
|
|
2771
|
+
var attrs = style.getDefinition().attributes,
|
|
2772
|
+
classes = attrs && attrs[ 'class' ];
|
|
2773
|
+
|
|
2774
|
+
return classes ? classes.split( /\s+/ ) : null;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
// [IE] Force keeping focus because IE sometimes forgets to fire focus on main editable
|
|
2778
|
+
// when blurring nested editable.
|
|
2779
|
+
// @context widget
|
|
2780
|
+
function onEditableBlur() {
|
|
2781
|
+
var active = CKEDITOR.document.getActive(),
|
|
2782
|
+
editor = this.editor,
|
|
2783
|
+
editable = editor.editable();
|
|
2784
|
+
|
|
2785
|
+
// If focus stays within editor override blur and set currentActive because it should be
|
|
2786
|
+
// automatically changed to editable on editable#focus but it is not fired.
|
|
2787
|
+
if ( ( editable.isInline() ? editable : editor.document.getWindow().getFrame() ).equals( active ) )
|
|
2788
|
+
editor.focusManager.focus( editable );
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
// Force selectionChange when editable was focused.
|
|
2792
|
+
// Similar to hack in selection.js#~620.
|
|
2793
|
+
// @context widget
|
|
2794
|
+
function onEditableFocus() {
|
|
2795
|
+
// Gecko does not support 'DOMFocusIn' event on which we unlock selection
|
|
2796
|
+
// in selection.js to prevent selection locking when entering nested editables.
|
|
2797
|
+
if ( CKEDITOR.env.gecko )
|
|
2798
|
+
this.editor.unlockSelection();
|
|
2799
|
+
|
|
2800
|
+
// We don't need to force selectionCheck on Webkit, because on Webkit
|
|
2801
|
+
// we do that on DOMFocusIn in selection.js.
|
|
2802
|
+
if ( !CKEDITOR.env.webkit ) {
|
|
2803
|
+
this.editor.forceNextSelectionCheck();
|
|
2804
|
+
this.editor.selectionChange( 1 );
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
// Setup listener on widget#data which will update (remove/add) classes
|
|
2809
|
+
// by comparing newly set classes with the old ones.
|
|
2810
|
+
function setupDataClassesListener( widget ) {
|
|
2811
|
+
// Note: previousClasses and newClasses may be null!
|
|
2812
|
+
// Tip: for ( cl in null ) is correct.
|
|
2813
|
+
var previousClasses = null;
|
|
2814
|
+
|
|
2815
|
+
widget.on( 'data', function() {
|
|
2816
|
+
var newClasses = this.data.classes,
|
|
2817
|
+
cl;
|
|
2818
|
+
|
|
2819
|
+
// When setting new classes one need to remember
|
|
2820
|
+
// that he must break reference.
|
|
2821
|
+
if ( previousClasses == newClasses )
|
|
2822
|
+
return;
|
|
2823
|
+
|
|
2824
|
+
for ( cl in previousClasses ) {
|
|
2825
|
+
// Avoid removing and adding classes again.
|
|
2826
|
+
if ( !( newClasses && newClasses[ cl ] ) )
|
|
2827
|
+
this.removeClass( cl );
|
|
2828
|
+
}
|
|
2829
|
+
for ( cl in newClasses )
|
|
2830
|
+
this.addClass( cl );
|
|
2831
|
+
|
|
2832
|
+
previousClasses = newClasses;
|
|
2833
|
+
} );
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
function setupDragHandler( widget ) {
|
|
2837
|
+
if ( !widget.draggable )
|
|
2838
|
+
return;
|
|
2839
|
+
|
|
2840
|
+
var editor = widget.editor,
|
|
2841
|
+
container = widget.wrapper.findOne( '.cke_widget_drag_handler_container' ),
|
|
2842
|
+
img;
|
|
2843
|
+
|
|
2844
|
+
// Reuse drag handler if already exists (#11281).
|
|
2845
|
+
if ( container )
|
|
2846
|
+
img = container.findOne( 'img' );
|
|
2847
|
+
else {
|
|
2848
|
+
container = new CKEDITOR.dom.element( 'span', editor.document );
|
|
2849
|
+
container.setAttributes( {
|
|
2850
|
+
'class': 'cke_reset cke_widget_drag_handler_container',
|
|
2851
|
+
// Split background and background-image for IE8 which will break on rgba().
|
|
2852
|
+
style: 'background:rgba(220,220,220,0.5);background-image:url(' + editor.plugins.widget.path + 'images/handle.png)'
|
|
2853
|
+
} );
|
|
2854
|
+
|
|
2855
|
+
img = new CKEDITOR.dom.element( 'img', editor.document );
|
|
2856
|
+
img.setAttributes( {
|
|
2857
|
+
'class': 'cke_reset cke_widget_drag_handler',
|
|
2858
|
+
'data-cke-widget-drag-handler': '1',
|
|
2859
|
+
src: CKEDITOR.tools.transparentImageData,
|
|
2860
|
+
width: DRAG_HANDLER_SIZE,
|
|
2861
|
+
title: editor.lang.widget.move,
|
|
2862
|
+
height: DRAG_HANDLER_SIZE
|
|
2863
|
+
} );
|
|
2864
|
+
widget.inline && img.setAttribute( 'draggable', 'true' );
|
|
2865
|
+
|
|
2866
|
+
container.append( img );
|
|
2867
|
+
widget.wrapper.append( container );
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
widget.wrapper.on( 'mouseenter', widget.updateDragHandlerPosition, widget );
|
|
2871
|
+
setTimeout( function() {
|
|
2872
|
+
widget.on( 'data', widget.updateDragHandlerPosition, widget );
|
|
2873
|
+
}, 50 );
|
|
2874
|
+
|
|
2875
|
+
if ( widget.inline ) {
|
|
2876
|
+
img.on( 'dragstart', function( evt ) {
|
|
2877
|
+
evt.data.$.dataTransfer.setData( 'text', JSON.stringify( { type: 'cke-widget', editor: editor.name, id: widget.id } ) );
|
|
2878
|
+
} );
|
|
2879
|
+
} else
|
|
2880
|
+
img.on( 'mousedown', onBlockWidgetDrag, widget );
|
|
2881
|
+
|
|
2882
|
+
widget.dragHandlerContainer = container;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
function onBlockWidgetDrag() {
|
|
2886
|
+
var finder = this.repository.finder,
|
|
2887
|
+
locator = this.repository.locator,
|
|
2888
|
+
liner = this.repository.liner,
|
|
2889
|
+
editor = this.editor,
|
|
2890
|
+
editable = editor.editable(),
|
|
2891
|
+
listeners = [],
|
|
2892
|
+
sorted = [],
|
|
2893
|
+
|
|
2894
|
+
// Harvest all possible relations and display some closest.
|
|
2895
|
+
relations = finder.greedySearch(),
|
|
2896
|
+
|
|
2897
|
+
buffer = CKEDITOR.tools.eventsBuffer( 50, function() {
|
|
2898
|
+
locations = locator.locate( relations );
|
|
2899
|
+
|
|
2900
|
+
// There's only a single line displayed for D&D.
|
|
2901
|
+
sorted = locator.sort( y, 1 );
|
|
2902
|
+
|
|
2903
|
+
if ( sorted.length ) {
|
|
2904
|
+
liner.prepare( relations, locations );
|
|
2905
|
+
liner.placeLine( sorted[ 0 ] );
|
|
2906
|
+
liner.cleanup();
|
|
2907
|
+
}
|
|
2908
|
+
} ),
|
|
2909
|
+
|
|
2910
|
+
locations, y;
|
|
2911
|
+
|
|
2912
|
+
// Let's have the "dragging cursor" over entire editable.
|
|
2913
|
+
editable.addClass( 'cke_widget_dragging' );
|
|
2914
|
+
|
|
2915
|
+
// Cache mouse position so it is re-used in events buffer.
|
|
2916
|
+
listeners.push( editable.on( 'mousemove', function( evt ) {
|
|
2917
|
+
y = evt.data.$.clientY;
|
|
2918
|
+
buffer.input();
|
|
2919
|
+
} ) );
|
|
2920
|
+
|
|
2921
|
+
function onMouseUp() {
|
|
2922
|
+
var l;
|
|
2923
|
+
|
|
2924
|
+
buffer.reset();
|
|
2925
|
+
|
|
2926
|
+
// Stop observing events.
|
|
2927
|
+
while ( ( l = listeners.pop() ) )
|
|
2928
|
+
l.removeListener();
|
|
2929
|
+
|
|
2930
|
+
onBlockWidgetDrop.call( this, sorted );
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
// Mouseup means "drop". This is when the widget is being detached
|
|
2934
|
+
// from DOM and placed at range determined by the line (location).
|
|
2935
|
+
listeners.push( editor.document.once( 'mouseup', onMouseUp, this ) );
|
|
2936
|
+
|
|
2937
|
+
// Mouseup may occur when user hovers the line, which belongs to
|
|
2938
|
+
// the outer document. This is, of course, a valid listener too.
|
|
2939
|
+
listeners.push( CKEDITOR.document.once( 'mouseup', onMouseUp, this ) );
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
function onBlockWidgetDrop( sorted ) {
|
|
2943
|
+
var finder = this.repository.finder,
|
|
2944
|
+
liner = this.repository.liner,
|
|
2945
|
+
editor = this.editor,
|
|
2946
|
+
editable = this.editor.editable();
|
|
2947
|
+
|
|
2948
|
+
if ( !CKEDITOR.tools.isEmpty( liner.visible ) ) {
|
|
2949
|
+
// Retrieve range for the closest location.
|
|
2950
|
+
var range = finder.getRange( sorted[ 0 ] );
|
|
2951
|
+
|
|
2952
|
+
// Focus widget (it could lost focus after mousedown+mouseup)
|
|
2953
|
+
// and save this state as the one where we want to be taken back when undoing.
|
|
2954
|
+
this.focus();
|
|
2955
|
+
editor.fire( 'saveSnapshot' );
|
|
2956
|
+
// Group all following operations in one snapshot.
|
|
2957
|
+
editor.fire( 'lockSnapshot', { dontUpdate: 1 } );
|
|
2958
|
+
|
|
2959
|
+
// Reset the fake selection, which will be invalidated by insertElementIntoRange.
|
|
2960
|
+
// This avoids a situation when getSelection() still returns a fake selection made
|
|
2961
|
+
// on widget which in the meantime has been moved to other place. That could cause
|
|
2962
|
+
// an error thrown e.g. by saveSnapshot or stateUpdater.
|
|
2963
|
+
editor.getSelection().reset();
|
|
2964
|
+
|
|
2965
|
+
// Attach widget at the place determined by range.
|
|
2966
|
+
editable.insertElementIntoRange( this.wrapper, range );
|
|
2967
|
+
|
|
2968
|
+
// Focus again the dropped widget.
|
|
2969
|
+
this.focus();
|
|
2970
|
+
|
|
2971
|
+
// Unlock snapshot and save new one, which will contain all changes done
|
|
2972
|
+
// in this method.
|
|
2973
|
+
editor.fire( 'unlockSnapshot' );
|
|
2974
|
+
editor.fire( 'saveSnapshot' );
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
// Clean-up custom cursor for editable.
|
|
2978
|
+
editable.removeClass( 'cke_widget_dragging' );
|
|
2979
|
+
|
|
2980
|
+
// Clean-up all remaining lines.
|
|
2981
|
+
liner.hideVisible();
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
function setupEditables( widget ) {
|
|
2985
|
+
var editableName,
|
|
2986
|
+
editableDef,
|
|
2987
|
+
definedEditables = widget.editables;
|
|
2988
|
+
|
|
2989
|
+
widget.editables = {};
|
|
2990
|
+
|
|
2991
|
+
if ( !widget.editables )
|
|
2992
|
+
return;
|
|
2993
|
+
|
|
2994
|
+
for ( editableName in definedEditables ) {
|
|
2995
|
+
editableDef = definedEditables[ editableName ];
|
|
2996
|
+
widget.initEditable( editableName, typeof editableDef == 'string' ? { selector: editableDef } : editableDef );
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
function setupMask( widget ) {
|
|
3001
|
+
if ( !widget.mask )
|
|
3002
|
+
return;
|
|
3003
|
+
|
|
3004
|
+
// Reuse mask if already exists (#11281).
|
|
3005
|
+
var img = widget.wrapper.findOne( '.cke_widget_mask' );
|
|
3006
|
+
|
|
3007
|
+
if ( !img ) {
|
|
3008
|
+
img = new CKEDITOR.dom.element( 'img', widget.editor.document );
|
|
3009
|
+
img.setAttributes( {
|
|
3010
|
+
src: CKEDITOR.tools.transparentImageData,
|
|
3011
|
+
'class': 'cke_reset cke_widget_mask'
|
|
3012
|
+
} );
|
|
3013
|
+
widget.wrapper.append( img );
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
widget.mask = img;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
// Replace parts object containing:
|
|
3020
|
+
// partName => selector pairs
|
|
3021
|
+
// with:
|
|
3022
|
+
// partName => element pairs
|
|
3023
|
+
function setupParts( widget ) {
|
|
3024
|
+
if ( widget.parts ) {
|
|
3025
|
+
var parts = {},
|
|
3026
|
+
el, partName;
|
|
3027
|
+
|
|
3028
|
+
for ( partName in widget.parts ) {
|
|
3029
|
+
el = widget.wrapper.findOne( widget.parts[ partName ] );
|
|
3030
|
+
parts[ partName ] = el;
|
|
3031
|
+
}
|
|
3032
|
+
widget.parts = parts;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
function setupWidget( widget, widgetDef ) {
|
|
3037
|
+
setupWrapper( widget );
|
|
3038
|
+
setupParts( widget );
|
|
3039
|
+
setupEditables( widget );
|
|
3040
|
+
setupMask( widget );
|
|
3041
|
+
setupDragHandler( widget );
|
|
3042
|
+
setupDataClassesListener( widget );
|
|
3043
|
+
|
|
3044
|
+
// #11145: [IE8] Non-editable content of widget is draggable.
|
|
3045
|
+
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) {
|
|
3046
|
+
widget.wrapper.on( 'dragstart', function( evt ) {
|
|
3047
|
+
var target = evt.data.getTarget();
|
|
3048
|
+
|
|
3049
|
+
// Allow text dragging inside nested editables or dragging inline widget's drag handler.
|
|
3050
|
+
if ( !getNestedEditable( widget, target ) && !( widget.inline && isDomDragHandler( target ) ) )
|
|
3051
|
+
evt.data.preventDefault();
|
|
3052
|
+
} );
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
widget.wrapper.removeClass( 'cke_widget_new' );
|
|
3056
|
+
widget.element.addClass( 'cke_widget_element' );
|
|
3057
|
+
|
|
3058
|
+
widget.on( 'key', function( evt ) {
|
|
3059
|
+
var keyCode = evt.data.keyCode;
|
|
3060
|
+
|
|
3061
|
+
// ENTER.
|
|
3062
|
+
if ( keyCode == 13 )
|
|
3063
|
+
widget.edit();
|
|
3064
|
+
// CTRL+C or CTRL+X.
|
|
3065
|
+
else if ( keyCode == CKEDITOR.CTRL + 67 || keyCode == CKEDITOR.CTRL + 88 ) {
|
|
3066
|
+
copySingleWidget( widget, keyCode == CKEDITOR.CTRL + 88 );
|
|
3067
|
+
return; // Do not preventDefault.
|
|
3068
|
+
}
|
|
3069
|
+
// Pass chosen keystrokes to other plugins or default fake sel handlers.
|
|
3070
|
+
// Pass all CTRL/ALT keystrokes.
|
|
3071
|
+
else if ( keyCode in keystrokesNotBlockedByWidget || ( CKEDITOR.CTRL & keyCode ) || ( CKEDITOR.ALT & keyCode ) )
|
|
3072
|
+
return;
|
|
3073
|
+
|
|
3074
|
+
return false;
|
|
3075
|
+
}, null, null, 999 );
|
|
3076
|
+
// Listen with high priority so it's possible
|
|
3077
|
+
// to overwrite this callback.
|
|
3078
|
+
|
|
3079
|
+
widget.on( 'doubleclick', function( evt ) {
|
|
3080
|
+
widget.edit();
|
|
3081
|
+
} );
|
|
3082
|
+
|
|
3083
|
+
if ( widgetDef.data )
|
|
3084
|
+
widget.on( 'data', widgetDef.data );
|
|
3085
|
+
|
|
3086
|
+
if ( widgetDef.edit )
|
|
3087
|
+
widget.on( 'edit', widgetDef.edit );
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
function setupWidgetData( widget, startupData ) {
|
|
3091
|
+
var widgetDataAttr = widget.element.data( 'cke-widget-data' );
|
|
3092
|
+
|
|
3093
|
+
if ( widgetDataAttr )
|
|
3094
|
+
widget.setData( JSON.parse( decodeURIComponent( widgetDataAttr ) ) );
|
|
3095
|
+
if ( startupData )
|
|
3096
|
+
widget.setData( startupData );
|
|
3097
|
+
|
|
3098
|
+
// Populate classes if they are not preset.
|
|
3099
|
+
if ( !widget.data.classes )
|
|
3100
|
+
widget.setData( 'classes', widget.getClasses() );
|
|
3101
|
+
|
|
3102
|
+
// Unblock data and...
|
|
3103
|
+
widget.dataReady = true;
|
|
3104
|
+
|
|
3105
|
+
// Write data to element because this was blocked when data wasn't ready.
|
|
3106
|
+
writeDataToElement( widget );
|
|
3107
|
+
|
|
3108
|
+
// Fire data event first time, because this was blocked when data wasn't ready.
|
|
3109
|
+
widget.fire( 'data', widget.data );
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
function setupWrapper( widget ) {
|
|
3113
|
+
// Retrieve widget wrapper. Assign an id to it.
|
|
3114
|
+
var wrapper = widget.wrapper = widget.element.getParent();
|
|
3115
|
+
wrapper.setAttribute( 'data-cke-widget-id', widget.id );
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
function writeDataToElement( widget ) {
|
|
3119
|
+
widget.element.data( 'cke-widget-data', encodeURIComponent( JSON.stringify( widget.data ) ) );
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
//
|
|
3123
|
+
// WIDGET STYLE HANDLER ---------------------------------------------------
|
|
3124
|
+
//
|
|
3125
|
+
|
|
3126
|
+
( function() {
|
|
3127
|
+
|
|
3128
|
+
/**
|
|
3129
|
+
* The class representing a widget style. It is an {@link CKEDITOR#STYLE_OBJECT object} like
|
|
3130
|
+
* the styles handler for widgets.
|
|
3131
|
+
*
|
|
3132
|
+
* **Note:** This custom style handler does not support all methods of the {@link CKEDITOR.style} class.
|
|
3133
|
+
* Not supported methods: {@link #applyToRange}, {@link #removeFromRange}, {@link #applyToObject}.
|
|
3134
|
+
*
|
|
3135
|
+
* @since 4.4
|
|
3136
|
+
* @class CKEDITOR.style.customHandlers.widget
|
|
3137
|
+
* @extends CKEDITOR.style
|
|
3138
|
+
*/
|
|
3139
|
+
CKEDITOR.style.addCustomHandler( {
|
|
3140
|
+
type: 'widget',
|
|
3141
|
+
|
|
3142
|
+
setup: function( styleDefinition ) {
|
|
3143
|
+
/**
|
|
3144
|
+
* The name of widget to which this style can be applied.
|
|
3145
|
+
* It is extracted from style definition's `widget` property.
|
|
3146
|
+
*
|
|
3147
|
+
* @property {String} widget
|
|
3148
|
+
*/
|
|
3149
|
+
this.widget = styleDefinition.widget;
|
|
3150
|
+
},
|
|
3151
|
+
|
|
3152
|
+
apply: function( editor ) {
|
|
3153
|
+
// Before CKEditor 4.4 wasn't a required argument, so we need to
|
|
3154
|
+
// handle a case when it wasn't provided.
|
|
3155
|
+
if ( !( editor instanceof CKEDITOR.editor ) )
|
|
3156
|
+
return;
|
|
3157
|
+
|
|
3158
|
+
// Theoretically we could bypass checkApplicable, get widget from
|
|
3159
|
+
// widgets.focused and check its name, what would be faster, but then
|
|
3160
|
+
// this custom style would work differently than the default style
|
|
3161
|
+
// which checks if it's applicable before applying or removeing itself.
|
|
3162
|
+
if ( this.checkApplicable( editor.elementPath(), editor ) )
|
|
3163
|
+
editor.widgets.focused.applyStyle( this );
|
|
3164
|
+
},
|
|
3165
|
+
|
|
3166
|
+
remove: function( editor ) {
|
|
3167
|
+
// Before CKEditor 4.4 wasn't a required argument, so we need to
|
|
3168
|
+
// handle a case when it wasn't provided.
|
|
3169
|
+
if ( !( editor instanceof CKEDITOR.editor ) )
|
|
3170
|
+
return;
|
|
3171
|
+
|
|
3172
|
+
if ( this.checkApplicable( editor.elementPath(), editor ) )
|
|
3173
|
+
editor.widgets.focused.removeStyle( this );
|
|
3174
|
+
},
|
|
3175
|
+
|
|
3176
|
+
checkActive: function( elementPath, editor ) {
|
|
3177
|
+
return this.checkElementMatch( elementPath.lastElement, 0, editor );
|
|
3178
|
+
},
|
|
3179
|
+
|
|
3180
|
+
checkApplicable: function( elementPath, editor, filter ) {
|
|
3181
|
+
// Before CKEditor 4.4 wasn't a required argument, so we need to
|
|
3182
|
+
// handle a case when it wasn't provided.
|
|
3183
|
+
if ( !( editor instanceof CKEDITOR.editor ) )
|
|
3184
|
+
return false;
|
|
3185
|
+
|
|
3186
|
+
return this.checkElement( elementPath.lastElement, editor );
|
|
3187
|
+
},
|
|
3188
|
+
|
|
3189
|
+
checkElementMatch: checkElementMatch,
|
|
3190
|
+
|
|
3191
|
+
checkElementRemovable: checkElementMatch,
|
|
3192
|
+
|
|
3193
|
+
/**
|
|
3194
|
+
* Checks if an element is a {@link CKEDITOR.plugins.widget#wrapper wrapper} of a
|
|
3195
|
+
* widget whose name matches the {@link #widget widget name} specified in the style definition.
|
|
3196
|
+
*
|
|
3197
|
+
* @param {CKEDITOR.dom.element} element
|
|
3198
|
+
* @param {CKEDITOR.editor} editor
|
|
3199
|
+
* @returns {Boolean}
|
|
3200
|
+
*/
|
|
3201
|
+
checkElement: function( element, editor ) {
|
|
3202
|
+
if ( !isDomWidgetWrapper( element ) )
|
|
3203
|
+
return false;
|
|
3204
|
+
|
|
3205
|
+
var widgetElement = element.getFirst( isDomWidgetElement );
|
|
3206
|
+
return widgetElement && widgetElement.data( 'widget' ) == this.widget;
|
|
3207
|
+
},
|
|
3208
|
+
|
|
3209
|
+
buildPreview: function( label ) {
|
|
3210
|
+
return label || this._.definition.name;
|
|
3211
|
+
},
|
|
3212
|
+
|
|
3213
|
+
/**
|
|
3214
|
+
* Returns allowed content rules which should be registered for this style.
|
|
3215
|
+
* Uses widget's {@link CKEDITOR.plugins.widget.definition#styleableElements} to make a rule
|
|
3216
|
+
* allowing classes on specified elements or use widget's
|
|
3217
|
+
* {@link CKEDITOR.plugins.widget.definition#styleToAllowedContentRules} method to transform a style
|
|
3218
|
+
* into allowed content rules.
|
|
3219
|
+
*
|
|
3220
|
+
* @param {CKEDITOR.editor} The editor instance.
|
|
3221
|
+
* @returns {CKEDITOR.filter.allowedContentRules}
|
|
3222
|
+
*/
|
|
3223
|
+
toAllowedContentRules: function( editor ) {
|
|
3224
|
+
if ( !editor )
|
|
3225
|
+
return null;
|
|
3226
|
+
|
|
3227
|
+
var widgetDef = editor.widgets.registered[ this.widget ],
|
|
3228
|
+
classes,
|
|
3229
|
+
rule = {};
|
|
3230
|
+
|
|
3231
|
+
if ( !widgetDef )
|
|
3232
|
+
return null;
|
|
3233
|
+
|
|
3234
|
+
if ( widgetDef.styleableElements ) {
|
|
3235
|
+
classes = this.getClassesArray();
|
|
3236
|
+
if ( !classes )
|
|
3237
|
+
return null;
|
|
3238
|
+
|
|
3239
|
+
rule[ widgetDef.styleableElements ] = {
|
|
3240
|
+
classes: classes,
|
|
3241
|
+
propertiesOnly: true
|
|
3242
|
+
};
|
|
3243
|
+
return rule;
|
|
3244
|
+
}
|
|
3245
|
+
if ( widgetDef.styleToAllowedContentRules )
|
|
3246
|
+
return widgetDef.styleToAllowedContentRules( this );
|
|
3247
|
+
return null;
|
|
3248
|
+
},
|
|
3249
|
+
|
|
3250
|
+
/**
|
|
3251
|
+
* Returns classes defined in the style in form of an array.
|
|
3252
|
+
*
|
|
3253
|
+
* @returns {String[]}
|
|
3254
|
+
*/
|
|
3255
|
+
getClassesArray: function() {
|
|
3256
|
+
var classes = this._.definition.attributes && this._.definition.attributes[ 'class' ];
|
|
3257
|
+
|
|
3258
|
+
return classes ? CKEDITOR.tools.trim( classes ).split( /\s+/ ) : null;
|
|
3259
|
+
},
|
|
3260
|
+
|
|
3261
|
+
/**
|
|
3262
|
+
* Not implemented.
|
|
3263
|
+
*
|
|
3264
|
+
* @method applyToRange
|
|
3265
|
+
*/
|
|
3266
|
+
applyToRange: notImplemented,
|
|
3267
|
+
|
|
3268
|
+
/**
|
|
3269
|
+
* Not implemented.
|
|
3270
|
+
*
|
|
3271
|
+
* @method removeFromRange
|
|
3272
|
+
*/
|
|
3273
|
+
removeFromRange: notImplemented,
|
|
3274
|
+
|
|
3275
|
+
/**
|
|
3276
|
+
* Not implemented.
|
|
3277
|
+
*
|
|
3278
|
+
* @method applyToObject
|
|
3279
|
+
*/
|
|
3280
|
+
applyToObject: notImplemented
|
|
3281
|
+
} );
|
|
3282
|
+
|
|
3283
|
+
function notImplemented() {}
|
|
3284
|
+
|
|
3285
|
+
// @context style
|
|
3286
|
+
function checkElementMatch( element, fullMatch, editor ) {
|
|
3287
|
+
// Before CKEditor 4.4 wasn't a required argument, so we need to
|
|
3288
|
+
// handle a case when it wasn't provided.
|
|
3289
|
+
if ( !editor )
|
|
3290
|
+
return false;
|
|
3291
|
+
|
|
3292
|
+
if ( !this.checkElement( element, editor ) )
|
|
3293
|
+
return false;
|
|
3294
|
+
|
|
3295
|
+
var widget = editor.widgets.getByElement( element, true );
|
|
3296
|
+
return widget && widget.checkStyleActive( this );
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
} )();
|
|
3300
|
+
|
|
3301
|
+
//
|
|
3302
|
+
// EXPOSE PUBLIC API ------------------------------------------------------
|
|
3303
|
+
//
|
|
3304
|
+
|
|
3305
|
+
CKEDITOR.plugins.widget = Widget;
|
|
3306
|
+
Widget.repository = Repository;
|
|
3307
|
+
Widget.nestedEditable = NestedEditable;
|
|
3308
|
+
} )();
|
|
3309
|
+
|
|
3310
|
+
/**
|
|
3311
|
+
* An event fired when a widget definition is registered by the {@link CKEDITOR.plugins.widget.repository#add} method.
|
|
3312
|
+
* It is possible to modify the definition being registered.
|
|
3313
|
+
*
|
|
3314
|
+
* @event widgetDefinition
|
|
3315
|
+
* @member CKEDITOR.editor
|
|
3316
|
+
* @param {CKEDITOR.plugins.widget.definition} data Widget definition.
|
|
3317
|
+
*/
|
|
3318
|
+
|
|
3319
|
+
/**
|
|
3320
|
+
* This is an abstract class that describes the definition of a widget.
|
|
3321
|
+
* It is a type of {@link CKEDITOR.plugins.widget.repository#add} method's second argument.
|
|
3322
|
+
*
|
|
3323
|
+
* Widget instances inherit from registered widget definitions, although not in a prototypal way.
|
|
3324
|
+
* They are simply extended with corresponding widget definitions. Note that not all properties of
|
|
3325
|
+
* the widget definition become properties of a widget. Some, like {@link #data} or {@link #edit}, become
|
|
3326
|
+
* widget's events listeners.
|
|
3327
|
+
*
|
|
3328
|
+
* @class CKEDITOR.plugins.widget.definition
|
|
3329
|
+
* @abstract
|
|
3330
|
+
* @mixins CKEDITOR.feature
|
|
3331
|
+
*/
|
|
3332
|
+
|
|
3333
|
+
/**
|
|
3334
|
+
* Widget definition name. It is automatically set when the definition is
|
|
3335
|
+
* {@link CKEDITOR.plugins.widget.repository#add registered}.
|
|
3336
|
+
*
|
|
3337
|
+
* @property {String} name
|
|
3338
|
+
*/
|
|
3339
|
+
|
|
3340
|
+
/**
|
|
3341
|
+
* The method executed while initializing a widget, after a widget instance
|
|
3342
|
+
* is created, but before it is ready. It is executed before the first
|
|
3343
|
+
* {@link CKEDITOR.plugins.widget#event-data} is fired so it is common to
|
|
3344
|
+
* use the `init` method to populate widget data with information loaded from
|
|
3345
|
+
* the DOM, like for exmaple:
|
|
3346
|
+
*
|
|
3347
|
+
* init: function() {
|
|
3348
|
+
* this.setData( 'width', this.element.getStyle( 'width' ) );
|
|
3349
|
+
*
|
|
3350
|
+
* if ( this.parts.caption.getStyle( 'display' ) != 'none' )
|
|
3351
|
+
* this.setData( 'showCaption', true );
|
|
3352
|
+
* }
|
|
3353
|
+
*
|
|
3354
|
+
* @property {Function} init
|
|
3355
|
+
*/
|
|
3356
|
+
|
|
3357
|
+
/**
|
|
3358
|
+
* The function to be used to upcast an element to this widget or a
|
|
3359
|
+
* comma-separated list of upcast methods from the {@link #upcasts} object.
|
|
3360
|
+
*
|
|
3361
|
+
* The upcast function **is not** executed in the widget context (because the widget
|
|
3362
|
+
* does not exist yet) and two arguments are passed:
|
|
3363
|
+
*
|
|
3364
|
+
* * `element` ({@link CKEDITOR.htmlParser.element}) – The element to be checked.
|
|
3365
|
+
* * `data` (`Object`) – The object which can be extended with data which will then be passed to the widget.
|
|
3366
|
+
*
|
|
3367
|
+
* An element will be upcasted if a function returned `true` or an instance of
|
|
3368
|
+
* a {@link CKEDITOR.htmlParser.element} if upcasting meant DOM structure changes
|
|
3369
|
+
* (in this case the widget will be initialized on the returned element).
|
|
3370
|
+
*
|
|
3371
|
+
* @property {String/Function} upcast
|
|
3372
|
+
*/
|
|
3373
|
+
|
|
3374
|
+
/**
|
|
3375
|
+
* The object containing functions which can be used to upcast this widget.
|
|
3376
|
+
* Only those pointed by the {@link #upcast} property will be used.
|
|
3377
|
+
*
|
|
3378
|
+
* In most cases it is appropriate to use {@link #upcast} directly,
|
|
3379
|
+
* because majority of widgets need just one method.
|
|
3380
|
+
* However, in some cases the widget author may want to expose more than one variant
|
|
3381
|
+
* and then this property may be used.
|
|
3382
|
+
*
|
|
3383
|
+
* upcasts: {
|
|
3384
|
+
* // This function may upcast only figure elements.
|
|
3385
|
+
* figure: function() {
|
|
3386
|
+
* // ...
|
|
3387
|
+
* },
|
|
3388
|
+
* // This function may upcast only image elements.
|
|
3389
|
+
* image: function() {
|
|
3390
|
+
* // ...
|
|
3391
|
+
* },
|
|
3392
|
+
* // More variants...
|
|
3393
|
+
* }
|
|
3394
|
+
*
|
|
3395
|
+
* // Then, widget user may choose which upcast methods will be enabled.
|
|
3396
|
+
* editor.on( 'widgetDefinition', function( evt ) {
|
|
3397
|
+
* if ( evt.data.name == 'image' )
|
|
3398
|
+
* evt.data.upcast = 'figure,image'; // Use both methods.
|
|
3399
|
+
* } );
|
|
3400
|
+
*
|
|
3401
|
+
* @property {Object} upcasts
|
|
3402
|
+
*/
|
|
3403
|
+
|
|
3404
|
+
/**
|
|
3405
|
+
* The function to be used to downcast this widget or
|
|
3406
|
+
* a name of the downcast option from the {@link #downcasts} object.
|
|
3407
|
+
*
|
|
3408
|
+
* The downcast funciton will be executed in the {@link CKEDITOR.plugins.widget} context
|
|
3409
|
+
* and with `widgetElement` ({@link CKEDITOR.htmlParser.element}) argument which is
|
|
3410
|
+
* the widget's main element.
|
|
3411
|
+
*
|
|
3412
|
+
* The function may return an instance of the {@link CKEDITOR.htmlParser.node} class if the widget
|
|
3413
|
+
* needs to be downcasted to a different node than the widget's main element.
|
|
3414
|
+
*
|
|
3415
|
+
* @property {String/Function} downcast
|
|
3416
|
+
*/
|
|
3417
|
+
|
|
3418
|
+
/**
|
|
3419
|
+
* The object containing functions which can be used to downcast this widget.
|
|
3420
|
+
* Only the one pointed by the {@link #downcast} property will be used.
|
|
3421
|
+
*
|
|
3422
|
+
* In most cases it is appropriate to use {@link #downcast} directly,
|
|
3423
|
+
* because majority of widgets have just one variant of downcasting (or none at all).
|
|
3424
|
+
* However, in some cases the widget author may want to expose more than one variant
|
|
3425
|
+
* and then this property may be used.
|
|
3426
|
+
*
|
|
3427
|
+
* downcasts: {
|
|
3428
|
+
* // This downcast may transform the widget into the figure element.
|
|
3429
|
+
* figure: function() {
|
|
3430
|
+
* // ...
|
|
3431
|
+
* },
|
|
3432
|
+
* // This downcast may transform the widget into the image element with data-* attributes.
|
|
3433
|
+
* image: function() {
|
|
3434
|
+
* // ...
|
|
3435
|
+
* }
|
|
3436
|
+
* }
|
|
3437
|
+
*
|
|
3438
|
+
* // Then, the widget user may choose one of the downcast options when setting up his editor.
|
|
3439
|
+
* editor.on( 'widgetDefinition', function( evt ) {
|
|
3440
|
+
* if ( evt.data.name == 'image' )
|
|
3441
|
+
* evt.data.downcast = 'figure';
|
|
3442
|
+
* } );
|
|
3443
|
+
*
|
|
3444
|
+
* @property downcasts
|
|
3445
|
+
*/
|
|
3446
|
+
|
|
3447
|
+
/**
|
|
3448
|
+
* If set, it will be added as the {@link CKEDITOR.plugins.widget#event-edit} event listener.
|
|
3449
|
+
* This means that it will be executed when a widget is being edited.
|
|
3450
|
+
* See the {@link CKEDITOR.plugins.widget#method-edit} method.
|
|
3451
|
+
*
|
|
3452
|
+
* @property {Function} edit
|
|
3453
|
+
*/
|
|
3454
|
+
|
|
3455
|
+
/**
|
|
3456
|
+
* If set, it will be added as the {@link CKEDITOR.plugins.widget#event-data} event listener.
|
|
3457
|
+
* This means that it will be executed every time the {@link CKEDITOR.plugins.widget#property-data widget data} changes.
|
|
3458
|
+
*
|
|
3459
|
+
* @property {Function} data
|
|
3460
|
+
*/
|
|
3461
|
+
|
|
3462
|
+
/**
|
|
3463
|
+
* The method to be executed when the widget's command is executed in order to insert a new widget
|
|
3464
|
+
* (widget of this type is not focused). If not defined, then the default action will be
|
|
3465
|
+
* performed which means that:
|
|
3466
|
+
*
|
|
3467
|
+
* * An instance of the widget will be created in a detached {@link CKEDITOR.dom.documentFragment document fragment},
|
|
3468
|
+
* * The {@link CKEDITOR.plugins.widget#method-edit} method will be called to trigger widget editing,
|
|
3469
|
+
* * The widget element will be inserted into DOM.
|
|
3470
|
+
*
|
|
3471
|
+
* @property {Function} insert
|
|
3472
|
+
*/
|
|
3473
|
+
|
|
3474
|
+
/**
|
|
3475
|
+
* The name of a dialog window which will be opened on {@link CKEDITOR.plugins.widget#method-edit}.
|
|
3476
|
+
* If not defined, then the {@link CKEDITOR.plugins.widget#method-edit} method will not perform any action and
|
|
3477
|
+
* widget's command will insert a new widget without opening a dialog window first.
|
|
3478
|
+
*
|
|
3479
|
+
* @property {String} dialog
|
|
3480
|
+
*/
|
|
3481
|
+
|
|
3482
|
+
/**
|
|
3483
|
+
* The template which will be used to create a new widget element (when the widget's command is executed).
|
|
3484
|
+
* This string is populated with {@link #defaults default values} by using the {@link CKEDITOR.template} format.
|
|
3485
|
+
* Therefore it has to be a valid {@link CKEDITOR.template} argument.
|
|
3486
|
+
*
|
|
3487
|
+
* @property {String} template
|
|
3488
|
+
*/
|
|
3489
|
+
|
|
3490
|
+
/**
|
|
3491
|
+
* The data object which will be used to populate the data of a newly created widget.
|
|
3492
|
+
* See {@link CKEDITOR.plugins.widget#property-data}.
|
|
3493
|
+
*
|
|
3494
|
+
* defaults: {
|
|
3495
|
+
* showCaption: true,
|
|
3496
|
+
* align: 'none'
|
|
3497
|
+
* }
|
|
3498
|
+
*
|
|
3499
|
+
* @property defaults
|
|
3500
|
+
*/
|
|
3501
|
+
|
|
3502
|
+
/**
|
|
3503
|
+
* An object containing definitions of widget components (part name => CSS selector).
|
|
3504
|
+
*
|
|
3505
|
+
* parts: {
|
|
3506
|
+
* image: 'img',
|
|
3507
|
+
* caption: 'div.caption'
|
|
3508
|
+
* }
|
|
3509
|
+
*
|
|
3510
|
+
* @property parts
|
|
3511
|
+
*/
|
|
3512
|
+
|
|
3513
|
+
/**
|
|
3514
|
+
* An object containing definitions of nested editables (editable name => {@link CKEDITOR.plugins.widget.nestedEditable.definition}).
|
|
3515
|
+
*
|
|
3516
|
+
* editables: {
|
|
3517
|
+
* header: 'h1',
|
|
3518
|
+
* content: {
|
|
3519
|
+
* selector: 'div.content',
|
|
3520
|
+
* allowedContent: 'p strong em; a[!href]'
|
|
3521
|
+
* }
|
|
3522
|
+
* }
|
|
3523
|
+
*
|
|
3524
|
+
* @property editables
|
|
3525
|
+
*/
|
|
3526
|
+
|
|
3527
|
+
/**
|
|
3528
|
+
* Widget name displayed in elements path.
|
|
3529
|
+
*
|
|
3530
|
+
* @property {String} pathName
|
|
3531
|
+
*/
|
|
3532
|
+
|
|
3533
|
+
/**
|
|
3534
|
+
* If set to `true`, the widget's element will be covered with a transparent mask.
|
|
3535
|
+
* This will prevent its content from being clickable, which matters in case
|
|
3536
|
+
* of special elements like embedded Flash or iframes that generate a separate "context".
|
|
3537
|
+
*
|
|
3538
|
+
* @property {Boolean} mask
|
|
3539
|
+
*/
|
|
3540
|
+
|
|
3541
|
+
/**
|
|
3542
|
+
* If set to `true/false`, it will force the widget to be either an inline or a block widget.
|
|
3543
|
+
* If not set, the widget type will be determined from the widget element.
|
|
3544
|
+
*
|
|
3545
|
+
* Widget type influences whether a block (`div`) or an inline (`span`) element is used
|
|
3546
|
+
* for the wrapper.
|
|
3547
|
+
*
|
|
3548
|
+
* @property {Boolean} inline
|
|
3549
|
+
*/
|
|
3550
|
+
|
|
3551
|
+
/**
|
|
3552
|
+
* The label for the widget toolbar button.
|
|
3553
|
+
*
|
|
3554
|
+
* editor.widgets.add( 'simplebox', {
|
|
3555
|
+
* button: 'Create a simple box'
|
|
3556
|
+
* } );
|
|
3557
|
+
*
|
|
3558
|
+
* editor.widgets.add( 'simplebox', {
|
|
3559
|
+
* button: editor.lang.simplebox.title
|
|
3560
|
+
* } );
|
|
3561
|
+
*
|
|
3562
|
+
* @property {String} button
|
|
3563
|
+
*/
|
|
3564
|
+
|
|
3565
|
+
/**
|
|
3566
|
+
* Whether widget should be draggable. Defaults to `true`.
|
|
3567
|
+
* If set to `false` drag handler will not be displayed when hovering widget.
|
|
3568
|
+
*
|
|
3569
|
+
* @property {Boolean} draggable
|
|
3570
|
+
*/
|
|
3571
|
+
|
|
3572
|
+
/**
|
|
3573
|
+
* Names of element(s) (separated by spaces) for which the {@link CKEDITOR.filter} should allow classes
|
|
3574
|
+
* defined in the widget styles. For example if your widget is upcasted from a simple `<div>`
|
|
3575
|
+
* element, then in order to make it styleable you can set:
|
|
3576
|
+
*
|
|
3577
|
+
* editor.widgets.add( 'customWidget', {
|
|
3578
|
+
* upcast: function( element ) {
|
|
3579
|
+
* return element.name == 'div';
|
|
3580
|
+
* },
|
|
3581
|
+
*
|
|
3582
|
+
* // ...
|
|
3583
|
+
*
|
|
3584
|
+
* styleableElements: 'div'
|
|
3585
|
+
* } );
|
|
3586
|
+
*
|
|
3587
|
+
* Then, when the following style is defined:
|
|
3588
|
+
*
|
|
3589
|
+
* {
|
|
3590
|
+
* name: 'Thick border', type: 'widget', widget: 'customWidget',
|
|
3591
|
+
* attributes: { 'class': 'thickBorder' }
|
|
3592
|
+
* }
|
|
3593
|
+
*
|
|
3594
|
+
* a rule allowing the `thickBorder` class for `div` elements will be registered in the {@link CKEDITOR.filter}.
|
|
3595
|
+
*
|
|
3596
|
+
* If you need to have more freedom when transforming widget style to allowed content rules,
|
|
3597
|
+
* you can use the {@link #styleToAllowedContentRules} callback.
|
|
3598
|
+
*
|
|
3599
|
+
* @since 4.4
|
|
3600
|
+
* @property {String} styleableElements
|
|
3601
|
+
*/
|
|
3602
|
+
|
|
3603
|
+
/**
|
|
3604
|
+
* Function transforming custom widget's {@link CKEDITOR.style} instance into
|
|
3605
|
+
* {@link CKEDITOR.filter.allowedContentRules}. It may be used when a static
|
|
3606
|
+
* {@link #styleableElements} property is not enough to inform the {@link CKEDITOR.filter}
|
|
3607
|
+
* what HTML features should be enabled when allowing the given style.
|
|
3608
|
+
*
|
|
3609
|
+
* In most cases, when style's classes just have to be added to element name(s) used by
|
|
3610
|
+
* the widget element, it is recommended to use simpler {@link #styleableElements} property.
|
|
3611
|
+
*
|
|
3612
|
+
* In order to get parsed classes from the style definition you can use
|
|
3613
|
+
* {@link CKEDITOR.style.customHandlers.widget#getClassesArray}.
|
|
3614
|
+
*
|
|
3615
|
+
* For example, if you want to use the [object format of allowed content rules](#!/guide/dev_allowed_content_rules-section-object-format),
|
|
3616
|
+
* to specify `match` validator, your implementation could look like this:
|
|
3617
|
+
*
|
|
3618
|
+
* editor.widgets.add( 'customWidget', {
|
|
3619
|
+
* // ...
|
|
3620
|
+
*
|
|
3621
|
+
* styleToAllowedContentRules: funciton( style ) {
|
|
3622
|
+
* // Retrieve classes defined in the style.
|
|
3623
|
+
* var classes = style.getClassesArray();
|
|
3624
|
+
*
|
|
3625
|
+
* // Do something crazy - for example return allowed content rules in object format,
|
|
3626
|
+
* // with custom match property and propertiesOnly flag.
|
|
3627
|
+
* return {
|
|
3628
|
+
* h1: {
|
|
3629
|
+
* match: isWidgetElement,
|
|
3630
|
+
* propertiesOnly: true,
|
|
3631
|
+
* classes: classes
|
|
3632
|
+
* }
|
|
3633
|
+
* };
|
|
3634
|
+
* }
|
|
3635
|
+
* } );
|
|
3636
|
+
*
|
|
3637
|
+
* @since 4.4
|
|
3638
|
+
* @property {Function} styleToAllowedContentRules
|
|
3639
|
+
* @param {CKEDITOR.style.customHandlers.widget} style The style to be transformed.
|
|
3640
|
+
* @returns {CKEDITOR.filter.allowedContentRules}
|
|
3641
|
+
*/
|
|
3642
|
+
|
|
3643
|
+
/**
|
|
3644
|
+
* This is an abstract class that describes the definition of a widget's nested editable.
|
|
3645
|
+
* It is a type of values in the {@link CKEDITOR.plugins.widget.definition#editables} object.
|
|
3646
|
+
*
|
|
3647
|
+
* In the simplest case the definition is a string which is a CSS selector used to
|
|
3648
|
+
* find an element that will become a nested editable inside the widget. Note that
|
|
3649
|
+
* the widget element can be a nested editable, too.
|
|
3650
|
+
*
|
|
3651
|
+
* In the more advanced case a definition is an object with a required `selector` property.
|
|
3652
|
+
*
|
|
3653
|
+
* editables: {
|
|
3654
|
+
* header: 'h1',
|
|
3655
|
+
* content: {
|
|
3656
|
+
* selector: 'div.content',
|
|
3657
|
+
* allowedContent: 'p strong em; a[!href]'
|
|
3658
|
+
* }
|
|
3659
|
+
* }
|
|
3660
|
+
*
|
|
3661
|
+
* @class CKEDITOR.plugins.widget.nestedEditable.definition
|
|
3662
|
+
* @abstract
|
|
3663
|
+
*/
|
|
3664
|
+
|
|
3665
|
+
/**
|
|
3666
|
+
* The CSS selector used to find an element which will become a nested editable.
|
|
3667
|
+
*
|
|
3668
|
+
* @property {String} selector
|
|
3669
|
+
*/
|
|
3670
|
+
|
|
3671
|
+
/**
|
|
3672
|
+
* The [Advanced Content Filter](#!/guide/dev_advanced_content_filter) rules
|
|
3673
|
+
* which will be used to limit the content allowed in this nested editable.
|
|
3674
|
+
* This option is similar to {@link CKEDITOR.config#allowedContent} and one can
|
|
3675
|
+
* use it to limit the editor features available in the nested editable.
|
|
3676
|
+
*
|
|
3677
|
+
* @property {CKEDITOR.filter.allowedContentRules} allowedContent
|
|
3678
|
+
*/
|
|
3679
|
+
|
|
3680
|
+
/**
|
|
3681
|
+
* Nested editable name displayed in elements path.
|
|
3682
|
+
*
|
|
3683
|
+
* @property {String} pathName
|
|
3684
|
+
*/
|