locomotivecms 4.1.0 → 4.2.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +4 -3
- data/Rakefile +4 -0
- data/app/api/locomotive/api.rb +1 -1
- data/app/assets/config/locomotive_manifest.js +2 -0
- data/app/assets/javascripts/locomotive.js +2 -2
- data/app/assets/stylesheets/locomotive/account.scss +0 -1
- data/app/assets/stylesheets/locomotive/application.scss +0 -1
- data/app/assets/stylesheets/locomotive/live_editing_error.scss +0 -1
- data/app/assets/stylesheets/locomotive/live_editing_iframe.scss +0 -1
- data/app/controllers/locomotive/base_controller.rb +1 -1
- data/app/controllers/locomotive/concerns/authorization_controller.rb +1 -1
- data/app/controllers/locomotive/concerns/redirect_to_main_host_controller.rb +1 -1
- data/app/controllers/locomotive/concerns/site_dispatcher_controller.rb +1 -1
- data/app/controllers/locomotive/errors_controller.rb +1 -1
- data/app/controllers/locomotive/my_account_controller.rb +1 -1
- data/app/controllers/locomotive/passwords_controller.rb +3 -1
- data/app/controllers/locomotive/registrations_controller.rb +3 -1
- data/app/controllers/locomotive/sessions_controller.rb +3 -1
- data/app/controllers/locomotive/sites_controller.rb +1 -1
- data/app/helpers/locomotive/base_helper.rb +2 -2
- data/app/helpers/locomotive/custom_fields_helper.rb +2 -2
- data/app/helpers/locomotive/shared/activities_helper.rb +1 -1
- data/app/inputs/locomotive/api_key_input.rb +1 -1
- data/app/mailers/locomotive/notifications.rb +1 -1
- data/app/models/locomotive/account.rb +1 -1
- data/app/models/locomotive/concerns/account/api_key.rb +1 -1
- data/app/models/locomotive/concerns/content_entry/csv.rb +1 -1
- data/app/models/locomotive/concerns/content_entry/slug.rb +5 -5
- data/app/models/locomotive/concerns/content_type/entry_template.rb +3 -3
- data/app/models/locomotive/concerns/page/templatized.rb +1 -1
- data/app/models/locomotive/concerns/shared/json_attribute.rb +21 -2
- data/app/models/locomotive/concerns/site/routes.rb +1 -1
- data/app/models/locomotive/content_entry.rb +2 -2
- data/app/models/locomotive/page.rb +3 -3
- data/app/models/locomotive/section.rb +1 -1
- data/app/models/locomotive/snippet.rb +1 -1
- data/app/models/locomotive/translation.rb +1 -1
- data/app/services/locomotive/content_entry_import_service.rb +1 -1
- data/app/services/locomotive/content_entry_service.rb +2 -2
- data/app/services/locomotive/content_type_service.rb +13 -3
- data/app/services/locomotive/custom_field_service.rb +2 -2
- data/app/services/locomotive/editor_service.rb +1 -1
- data/app/uploaders/locomotive/base_uploader.rb +0 -10
- data/app/views/locomotive/dashboard/_activity.html.slim +1 -1
- data/app/views/locomotive/page_content/_edit.json.jbuilder +50 -0
- data/app/views/locomotive/page_content/edit.html.erb +1 -1
- data/app/views/locomotive/page_content/edit.json.jbuilder +52 -50
- data/config/initializers/devise.rb +5 -2
- data/lib/generators/locomotive/install/templates/locomotive.rb +0 -5
- data/lib/locomotive/action_controller/responder.rb +1 -1
- data/lib/locomotive/configuration.rb +2 -2
- data/lib/locomotive/dependencies.rb +0 -2
- data/lib/locomotive/engine.rb +14 -2
- data/lib/locomotive/mongoid/patches.rb +25 -65
- data/lib/locomotive/steam/services/api_entry_submission_service.rb +1 -0
- data/lib/locomotive/version.rb +1 -1
- data/vendor/assets/javascripts/locomotive/codemirror/addons/comment/comment.js +203 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/comment/continuecomment.js +85 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/dialog/dialog.js +157 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/display/autorefresh.js +47 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/display/fullscreen.js +41 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/display/panel.js +112 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/display/placeholder.js +62 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/display/rulers.js +51 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/closebrackets.js +195 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/closetag.js +169 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/continuelist.js +51 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/matchbrackets.js +120 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/matchtags.js +66 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/edit/trailingspace.js +27 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/brace-fold.js +105 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/comment-fold.js +59 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/foldcode.js +150 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/foldgutter.js +146 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/indent-fold.js +44 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/markdown-fold.js +49 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/fold/xml-fold.js +182 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/anyword-hint.js +41 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/css-hint.js +60 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/html-hint.js +348 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/javascript-hint.js +146 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/show-hint.js +437 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/sql-hint.js +271 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/hint/xml-hint.js +110 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/coffeescript-lint.js +41 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/css-lint.js +35 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/html-lint.js +46 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/javascript-lint.js +136 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/json-lint.js +31 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/lint.js +239 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/lint/yaml-lint.js +28 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/merge/merge.js +773 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/mode/loadmode.js +64 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/mode/multiplex.js +123 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/mode/overlay.js +85 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/mode/simple.js +213 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/runmode/colorize.js +40 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/runmode/runmode-standalone.js +157 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/runmode/runmode.js +72 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/runmode/runmode.node.js +179 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/scroll/annotatescrollbar.js +118 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/scroll/scrollpastend.js +48 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/scroll/simplescrollbars.js +152 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/search/jump-to-line.js +49 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/search/match-highlighter.js +146 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/search/matchesonscrollbar.js +97 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/search/search.js +249 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/search/searchcursor.js +189 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/selection/active-line.js +74 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/selection/mark-selection.js +118 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/selection/selection-pointer.js +98 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/tern/tern.js +701 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/tern/worker.js +44 -0
- data/vendor/assets/javascripts/locomotive/codemirror/addons/wrap/hardwrap.js +144 -0
- data/vendor/assets/javascripts/locomotive/codemirror/keymaps/emacs.js +412 -0
- data/vendor/assets/javascripts/locomotive/codemirror/keymaps/sublime.js +580 -0
- data/vendor/assets/javascripts/locomotive/codemirror/keymaps/vim.js +5065 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/apl.js +174 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/asciiarmor.js +73 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/asn.1.js +204 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/asterisk.js +196 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/brainfuck.js +85 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/clike.js +786 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/clojure.js +306 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/cmake.js +97 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/cobol.js +255 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/coffeescript.js +355 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/commonlisp.js +123 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/crystal.js +391 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/css.js +825 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/cypher.js +146 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/d.js +218 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/dart.js +157 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/diff.js +47 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/django.js +356 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/dockerfile.js +79 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/dtd.js +142 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/dylan.js +344 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ebnf.js +195 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ecl.js +206 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/eiffel.js +160 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/elm.js +205 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/erlang.js +618 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/factor.js +83 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/fcl.js +173 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/forth.js +180 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/fortran.js +188 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/gas.js +345 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/gfm.js +130 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/gherkin.js +178 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/go.js +185 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/groovy.js +230 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/haml.js +161 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/handlebars.js +62 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/haskell-literate.js +43 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/haskell.js +267 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/haxe.js +515 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/htmlembedded.js +28 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/htmlmixed.js +152 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/http.js +113 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/idl.js +290 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/jade.js +590 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/javascript.js +748 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/jinja2.js +142 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/jsx.js +147 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/julia.js +392 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/livescript.js +280 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/lua.js +159 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/markdown.js +797 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mathematica.js +176 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mbox.js +129 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mirc.js +193 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mllike.js +205 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/modelica.js +245 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mscgen.js +169 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/mumps.js +148 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/nginx.js +178 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/nsis.js +95 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ntriples.js +186 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/octave.js +135 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/oz.js +252 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/pascal.js +109 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/pegjs.js +114 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/perl.js +837 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/php.js +234 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/pig.js +178 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/powershell.js +396 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/properties.js +78 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/protobuf.js +68 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/puppet.js +220 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/python.js +340 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/q.js +139 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/r.js +164 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/rpm.js +109 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/rst.js +557 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ruby.js +285 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/rust.js +71 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/sas.js +315 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/sass.js +414 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/scheme.js +249 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/shell.js +139 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/sieve.js +193 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/slim.js +575 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/smalltalk.js +168 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/smarty.js +225 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/solr.js +104 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/soy.js +199 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/sparql.js +180 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/spreadsheet.js +112 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/sql.js +413 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/stex.js +251 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/stylus.js +769 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/swift.js +202 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/tcl.js +139 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/textile.js +469 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/tiddlywiki.js +308 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/tiki.js +312 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/toml.js +88 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/tornado.js +68 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/troff.js +84 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ttcn-cfg.js +214 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/ttcn.js +283 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/turtle.js +162 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/twig.js +141 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/vb.js +276 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/vbscript.js +350 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/velocity.js +201 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/verilog.js +537 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/vhdl.js +189 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/vue.js +69 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/webidl.js +195 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/xml.js +394 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/xquery.js +437 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/yacas.js +204 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/yaml-frontmatter.js +68 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/yaml.js +117 -0
- data/vendor/assets/javascripts/locomotive/codemirror/modes/z80.js +116 -0
- data/vendor/assets/javascripts/locomotive/codemirror.js +8922 -0
- data/vendor/assets/javascripts/locomotive/select2.js +436 -175
- data/vendor/assets/javascripts/locomotive/wysihtml5x-toolbar.js +1346 -564
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/dialog/dialog.css +32 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/display/fullscreen.css +6 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/fold/foldgutter.css +20 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/hint/show-hint.css +37 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/lint/lint.css +73 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/merge/merge.css +113 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/scroll/simplescrollbars.css +66 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/search/matchesonscrollbar.css +8 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/addons/tern/tern.css +87 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/modes/tiddlywiki.css +14 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/modes/tiki.css +26 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/3024-day.css +41 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/3024-night.css +39 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/abcdef.css +32 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/ambiance-mobile.css +5 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/ambiance.css +74 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/base16-dark.css +38 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/base16-light.css +38 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/bespin.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/blackboard.css +32 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/cobalt.css +25 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/colorforth.css +33 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/dracula.css +41 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/eclipse.css +23 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/elegant.css +13 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/erlang-dark.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/hopscotch.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/icecoder.css +43 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/isotope.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/lesser-dark.css +47 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/liquibyte.css +95 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/material.css +53 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/mbo.css +37 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/mdn-like.css +46 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/midnight.css +45 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/monokai.css +36 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/neat.css +12 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/neo.css +43 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/night.css +27 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/paraiso-dark.css +38 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/paraiso-light.css +38 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/pastel-on-dark.css +53 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/railscasts.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/rubyblue.css +25 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/seti.css +44 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/solarized.css +169 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/the-matrix.css +30 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/tomorrow-night-bright.css +35 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/tomorrow-night-eighties.css +38 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/ttcn.css +64 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/twilight.css +32 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/vibrant-ink.css +34 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/xq-dark.css +53 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/xq-light.css +43 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/yeti.css +44 -0
- data/vendor/assets/stylesheets/locomotive/codemirror/themes/zenburn.css +37 -0
- data/vendor/assets/stylesheets/locomotive/codemirror.css +347 -0
- data/vendor/assets/stylesheets/locomotive/select2.css +3 -6
- metadata +349 -128
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license wysihtml v0.5.
|
2
|
+
* @license wysihtml v0.5.5
|
3
3
|
* https://github.com/Voog/wysihtml
|
4
4
|
*
|
5
5
|
* Author: Christopher Blum (https://github.com/tiff)
|
@@ -10,7 +10,7 @@
|
|
10
10
|
*
|
11
11
|
*/
|
12
12
|
var wysihtml5 = {
|
13
|
-
version: "0.5.
|
13
|
+
version: "0.5.5",
|
14
14
|
|
15
15
|
// namespaces
|
16
16
|
commands: {},
|
@@ -24,6 +24,8 @@ var wysihtml5 = {
|
|
24
24
|
INVISIBLE_SPACE: "\uFEFF",
|
25
25
|
INVISIBLE_SPACE_REG_EXP: /\uFEFF/g,
|
26
26
|
|
27
|
+
VOID_ELEMENTS: "area, base, br, col, embed, hr, img, input, keygen, link, meta, param, source, track, wbr",
|
28
|
+
|
27
29
|
EMPTY_FUNCTION: function() {},
|
28
30
|
|
29
31
|
ELEMENT_NODE: 1,
|
@@ -38,7 +40,7 @@ var wysihtml5 = {
|
|
38
40
|
};
|
39
41
|
;wysihtml5.polyfills = function(win, doc) {
|
40
42
|
|
41
|
-
// TODO: in future try to replace most inline compability checks with polyfills for code readability
|
43
|
+
// TODO: in future try to replace most inline compability checks with polyfills for code readability
|
42
44
|
|
43
45
|
// IE8 SUPPORT BLOCK
|
44
46
|
// You can compile without all this if IE8 is not needed
|
@@ -133,20 +135,36 @@ var wysihtml5 = {
|
|
133
135
|
};
|
134
136
|
}
|
135
137
|
|
136
|
-
//
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
138
|
+
// closest and matches polyfill
|
139
|
+
// https://github.com/jonathantneal/closest
|
140
|
+
(function (ELEMENT) {
|
141
|
+
ELEMENT.matches = ELEMENT.matches || ELEMENT.mozMatchesSelector || ELEMENT.msMatchesSelector || ELEMENT.oMatchesSelector || ELEMENT.webkitMatchesSelector || function matches(selector) {
|
142
|
+
var
|
143
|
+
element = this,
|
144
|
+
elements = (element.document || element.ownerDocument).querySelectorAll(selector),
|
145
|
+
index = 0;
|
146
|
+
|
147
|
+
while (elements[index] && elements[index] !== element) {
|
148
|
+
++index;
|
149
|
+
}
|
150
|
+
|
151
|
+
return elements[index] ? true : false;
|
152
|
+
};
|
153
|
+
|
154
|
+
ELEMENT.closest = ELEMENT.closest || function closest(selector) {
|
155
|
+
var element = this;
|
156
|
+
|
157
|
+
while (element) {
|
158
|
+
if (element.matches(selector)) {
|
159
|
+
break;
|
160
|
+
}
|
161
|
+
|
162
|
+
element = element.parentElement;
|
163
|
+
}
|
164
|
+
|
165
|
+
return element;
|
148
166
|
};
|
149
|
-
}(
|
167
|
+
}(Element.prototype));
|
150
168
|
|
151
169
|
// Element.classList for ie8-9 (toggle all IE)
|
152
170
|
// source http://purl.eligrey.com/github/classList.js/blob/master/classList.js
|
@@ -405,7 +423,19 @@ var wysihtml5 = {
|
|
405
423
|
return all;
|
406
424
|
};
|
407
425
|
|
426
|
+
var isInDom = function(node) {
|
427
|
+
var doc = node.ownerDocument,
|
428
|
+
n = node;
|
429
|
+
|
430
|
+
do {
|
431
|
+
if (n === doc) {
|
432
|
+
return true;
|
433
|
+
}
|
434
|
+
n = n.parentNode;
|
435
|
+
} while(n);
|
408
436
|
|
437
|
+
return false;
|
438
|
+
};
|
409
439
|
|
410
440
|
var normalizeFix = function() {
|
411
441
|
var f = Node.prototype.normalize;
|
@@ -466,7 +496,7 @@ var wysihtml5 = {
|
|
466
496
|
aoffset = Array.prototype.indexOf.call(aelement.parentNode.childNodes, aelement);
|
467
497
|
}
|
468
498
|
|
469
|
-
if (anode && anode.parentNode && fnode && fnode.parentNode) {
|
499
|
+
if (isInDom(this) && anode && anode.parentNode && fnode && fnode.parentNode) {
|
470
500
|
r.setStart(anode, aoffset);
|
471
501
|
r.setEnd(fnode, foffset);
|
472
502
|
s.removeAllRanges();
|
@@ -475,19 +505,44 @@ var wysihtml5 = {
|
|
475
505
|
};
|
476
506
|
Node.prototype.normalize = nf;
|
477
507
|
};
|
478
|
-
|
508
|
+
|
479
509
|
var F = function() {
|
480
510
|
window.removeEventListener("load", F);
|
481
511
|
if ("Node" in window && "normalize" in Node.prototype && normalizeHasCaretError()) {
|
482
512
|
normalizeFix();
|
483
513
|
}
|
484
514
|
};
|
485
|
-
|
515
|
+
|
486
516
|
if (doc.readyState !== "complete") {
|
487
517
|
window.addEventListener("load", F);
|
488
518
|
} else {
|
489
519
|
F();
|
490
520
|
}
|
521
|
+
|
522
|
+
// CustomEvent for ie9 and up
|
523
|
+
function nativeCustomEventSupported() {
|
524
|
+
try {
|
525
|
+
var p = new CustomEvent('cat', {detail: {foo: 'bar'}});
|
526
|
+
return 'cat' === p.type && 'bar' === p.detail.foo;
|
527
|
+
} catch (e) {}
|
528
|
+
return false;
|
529
|
+
}
|
530
|
+
var customEventSupported = nativeCustomEventSupported();
|
531
|
+
|
532
|
+
// Polyfills CustomEvent object for IE9 and up
|
533
|
+
(function() {
|
534
|
+
if (!customEventSupported && "CustomEvent" in window) {
|
535
|
+
function CustomEvent(event, params) {
|
536
|
+
params = params || {bubbles: false, cancelable: false, detail: undefined};
|
537
|
+
var evt = doc.createEvent('CustomEvent');
|
538
|
+
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
539
|
+
return evt;
|
540
|
+
}
|
541
|
+
CustomEvent.prototype = win.Event.prototype;
|
542
|
+
win.CustomEvent = CustomEvent;
|
543
|
+
customEventSupported = true;
|
544
|
+
}
|
545
|
+
})();
|
491
546
|
};
|
492
547
|
|
493
548
|
wysihtml5.polyfills(window, document);
|
@@ -4304,7 +4359,7 @@ wysihtml5.polyfills(window, document);
|
|
4304
4359
|
win = null;
|
4305
4360
|
});
|
4306
4361
|
});
|
4307
|
-
|
4362
|
+
|
4308
4363
|
|
4309
4364
|
/*----------------------------------------------------------------------------------------------------------------*/
|
4310
4365
|
|
@@ -4335,7 +4390,8 @@ wysihtml5.polyfills(window, document);
|
|
4335
4390
|
}
|
4336
4391
|
|
4337
4392
|
return api;
|
4338
|
-
}, this)
|
4393
|
+
}, this);
|
4394
|
+
;/**
|
4339
4395
|
* Text range module for Rangy.
|
4340
4396
|
* Text-based manipulation and searching of ranges and selections.
|
4341
4397
|
*
|
@@ -4673,7 +4729,7 @@ wysihtml5.polyfills(window, document);
|
|
4673
4729
|
|
4674
4730
|
/*----------------------------------------------------------------------------------------------------------------*/
|
4675
4731
|
|
4676
|
-
|
4732
|
+
|
4677
4733
|
// "A block node is either an Element whose "display" property does not have
|
4678
4734
|
// resolved value "inline" or "inline-block" or "inline-table" or "none", or a
|
4679
4735
|
// Document, or a DocumentFragment."
|
@@ -6262,7 +6318,7 @@ wysihtml5.polyfills(window, document);
|
|
6262
6318
|
)
|
6263
6319
|
};
|
6264
6320
|
});
|
6265
|
-
|
6321
|
+
|
6266
6322
|
return rangy;
|
6267
6323
|
}, this);;/**
|
6268
6324
|
* Selection save and restore module for Rangy.
|
@@ -6513,7 +6569,7 @@ wysihtml5.polyfills(window, document);
|
|
6513
6569
|
removeMarkers: removeMarkers
|
6514
6570
|
});
|
6515
6571
|
});
|
6516
|
-
|
6572
|
+
|
6517
6573
|
return rangy;
|
6518
6574
|
}, this);;/*
|
6519
6575
|
Base.js, version 1.1a
|
@@ -6527,7 +6583,7 @@ var Base = function() {
|
|
6527
6583
|
|
6528
6584
|
Base.extend = function(_instance, _static) { // subclass
|
6529
6585
|
var extend = Base.prototype.extend;
|
6530
|
-
|
6586
|
+
|
6531
6587
|
// build the prototype
|
6532
6588
|
Base._prototyping = true;
|
6533
6589
|
var proto = new this;
|
@@ -6536,7 +6592,7 @@ Base.extend = function(_instance, _static) { // subclass
|
|
6536
6592
|
// call this method from any other method to invoke that method's ancestor
|
6537
6593
|
};
|
6538
6594
|
delete Base._prototyping;
|
6539
|
-
|
6595
|
+
|
6540
6596
|
// create the wrapper for the constructor function
|
6541
6597
|
//var constructor = proto.constructor.valueOf(); //-dean
|
6542
6598
|
var constructor = proto.constructor;
|
@@ -6551,7 +6607,7 @@ Base.extend = function(_instance, _static) { // subclass
|
|
6551
6607
|
}
|
6552
6608
|
}
|
6553
6609
|
};
|
6554
|
-
|
6610
|
+
|
6555
6611
|
// build the class interface
|
6556
6612
|
klass.ancestor = this;
|
6557
6613
|
klass.extend = this.extend;
|
@@ -6569,7 +6625,7 @@ Base.extend = function(_instance, _static) { // subclass
|
|
6569
6625
|
return klass;
|
6570
6626
|
};
|
6571
6627
|
|
6572
|
-
Base.prototype = {
|
6628
|
+
Base.prototype = {
|
6573
6629
|
extend: function(source, value) {
|
6574
6630
|
if (arguments.length > 1) { // extending with a name/value pair
|
6575
6631
|
var ancestor = this[source];
|
@@ -6628,7 +6684,7 @@ Base = Base.extend({
|
|
6628
6684
|
}, {
|
6629
6685
|
ancestor: Object,
|
6630
6686
|
version: "1.1",
|
6631
|
-
|
6687
|
+
|
6632
6688
|
forEach: function(object, block, context) {
|
6633
6689
|
for (var key in object) {
|
6634
6690
|
if (this.prototype[key] === undefined) {
|
@@ -6636,7 +6692,7 @@ Base = Base.extend({
|
|
6636
6692
|
}
|
6637
6693
|
}
|
6638
6694
|
},
|
6639
|
-
|
6695
|
+
|
6640
6696
|
implement: function() {
|
6641
6697
|
for (var i = 0; i < arguments.length; i++) {
|
6642
6698
|
if (typeof arguments[i] == "function") {
|
@@ -6649,7 +6705,7 @@ Base = Base.extend({
|
|
6649
6705
|
}
|
6650
6706
|
return this;
|
6651
6707
|
},
|
6652
|
-
|
6708
|
+
|
6653
6709
|
toString: function() {
|
6654
6710
|
return String(this.valueOf());
|
6655
6711
|
}
|
@@ -6660,10 +6716,11 @@ wysihtml5.browser = (function() {
|
|
6660
6716
|
var userAgent = navigator.userAgent,
|
6661
6717
|
testElement = document.createElement("div"),
|
6662
6718
|
// Browser sniffing is unfortunately needed since some behaviors are impossible to feature detect
|
6663
|
-
|
6664
|
-
|
6665
|
-
|
6666
|
-
|
6719
|
+
// We need to be extra careful about Microsoft as it shows increasing tendency of tainting its userAgent strings with false feathers
|
6720
|
+
isGecko = userAgent.indexOf("Gecko") !== -1 && userAgent.indexOf("KHTML") === -1 && !isIE(),
|
6721
|
+
isWebKit = userAgent.indexOf("AppleWebKit/") !== -1 && !isIE(),
|
6722
|
+
isChrome = userAgent.indexOf("Chrome/") !== -1 && !isIE(),
|
6723
|
+
isOpera = userAgent.indexOf("Opera/") !== -1 && !isIE();
|
6667
6724
|
|
6668
6725
|
function iosVersion(userAgent) {
|
6669
6726
|
return +((/ipad|iphone|ipod/.test(userAgent) && userAgent.match(/ os (\d+).+? like mac os x/)) || [undefined, 0])[1];
|
@@ -6833,14 +6890,15 @@ wysihtml5.browser = (function() {
|
|
6833
6890
|
*/
|
6834
6891
|
supportsCommand: (function() {
|
6835
6892
|
// Following commands are supported but contain bugs in some browsers
|
6893
|
+
// TODO: investigate if some of these bugs can be tested without altering selection on page, instead of targeting browsers and versions directly
|
6836
6894
|
var buggyCommands = {
|
6837
6895
|
// formatBlock fails with some tags (eg. <blockquote>)
|
6838
6896
|
"formatBlock": isIE(10, "<="),
|
6839
6897
|
// When inserting unordered or ordered lists in Firefox, Chrome or Safari, the current selection or line gets
|
6840
6898
|
// converted into a list (<ul><li>...</li></ul>, <ol><li>...</li></ol>)
|
6841
6899
|
// IE and Opera act a bit different here as they convert the entire content of the current block element into a list
|
6842
|
-
"insertUnorderedList": isIE(
|
6843
|
-
"insertOrderedList": isIE(
|
6900
|
+
"insertUnorderedList": isIE(),
|
6901
|
+
"insertOrderedList": isIE()
|
6844
6902
|
};
|
6845
6903
|
|
6846
6904
|
// Firefox throws errors for queryCommandSupported, so we have to build up our own object of supported commands
|
@@ -6997,6 +7055,11 @@ wysihtml5.browser = (function() {
|
|
6997
7055
|
return isIE();
|
6998
7056
|
},
|
6999
7057
|
|
7058
|
+
/* In IE when deleting with caret at the begining of LI, List get broken into half instead of merging the LI with previous */
|
7059
|
+
hasLiDeletingProblem: function() {
|
7060
|
+
return isIE();
|
7061
|
+
},
|
7062
|
+
|
7000
7063
|
hasUndoInContextMenu: function() {
|
7001
7064
|
return isGecko || isChrome || isOpera;
|
7002
7065
|
},
|
@@ -7030,6 +7093,12 @@ wysihtml5.browser = (function() {
|
|
7030
7093
|
return isWebKit;
|
7031
7094
|
},
|
7032
7095
|
|
7096
|
+
// In all webkit browsers there are some places where caret can not be placed at the end of blocks and directly before block level element
|
7097
|
+
// when startContainer is element.
|
7098
|
+
hasCaretBlockElementIssue: function() {
|
7099
|
+
return isWebKit;
|
7100
|
+
},
|
7101
|
+
|
7033
7102
|
supportsMutationEvents: function() {
|
7034
7103
|
return ("MutationEvent" in window);
|
7035
7104
|
},
|
@@ -7050,6 +7119,10 @@ wysihtml5.browser = (function() {
|
|
7050
7119
|
return ("styleFloat" in document.createElement("div").style) ? "styleFloat" : "cssFloat";
|
7051
7120
|
}
|
7052
7121
|
return key;
|
7122
|
+
},
|
7123
|
+
|
7124
|
+
usesControlRanges: function() {
|
7125
|
+
return document.body && "createControlRange" in document.body;
|
7053
7126
|
}
|
7054
7127
|
};
|
7055
7128
|
})();
|
@@ -7877,7 +7950,12 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
7877
7950
|
is: {
|
7878
7951
|
emptyTextNode: function(ignoreWhitespace) {
|
7879
7952
|
var regx = ignoreWhitespace ? (/^\s*$/g) : (/^[\r\n]*$/g);
|
7880
|
-
return node.nodeType === wysihtml5.TEXT_NODE && (regx).test(node.data);
|
7953
|
+
return node && node.nodeType === wysihtml5.TEXT_NODE && (regx).test(node.data);
|
7954
|
+
},
|
7955
|
+
|
7956
|
+
// Returns if node is the rangy selection bookmark element (that must not be taken into account in most situatons and is removed on selection restoring)
|
7957
|
+
rangyBookmark: function() {
|
7958
|
+
return node && node.nodeType === 1 && node.classList.contains('rangySelectionBoundary');
|
7881
7959
|
},
|
7882
7960
|
|
7883
7961
|
visible: function() {
|
@@ -7889,6 +7967,20 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
7889
7967
|
}
|
7890
7968
|
}
|
7891
7969
|
return isVisible;
|
7970
|
+
},
|
7971
|
+
lineBreak: function() {
|
7972
|
+
return node && node.nodeType === 1 && node.nodeName === "BR";
|
7973
|
+
},
|
7974
|
+
block: function() {
|
7975
|
+
return node && node.nodeType === 1 && node.ownerDocument.defaultView.getComputedStyle(node).display === "block";
|
7976
|
+
},
|
7977
|
+
// Void elements are elemens that can not have content
|
7978
|
+
// In most cases browsers should solve the cases for you when you try to insert content into those,
|
7979
|
+
// but IE does not and it is not nice to do so anyway.
|
7980
|
+
voidElement: function() {
|
7981
|
+
return wysihtml5.dom.domNode(node).test({
|
7982
|
+
query: wysihtml5.VOID_ELEMENTS
|
7983
|
+
});
|
7892
7984
|
}
|
7893
7985
|
},
|
7894
7986
|
|
@@ -7896,18 +7988,19 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
7896
7988
|
prev: function(options) {
|
7897
7989
|
var prevNode = node.previousSibling,
|
7898
7990
|
types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
|
7899
|
-
|
7991
|
+
|
7900
7992
|
if (!prevNode) {
|
7901
7993
|
return null;
|
7902
7994
|
}
|
7903
7995
|
|
7904
7996
|
if (
|
7997
|
+
wysihtml5.dom.domNode(prevNode).is.rangyBookmark() || // is Rangy temporary boomark element (bypass)
|
7905
7998
|
(!wysihtml5.lang.array(types).contains(prevNode.nodeType)) || // nodeTypes check.
|
7906
7999
|
(options && options.ignoreBlankTexts && wysihtml5.dom.domNode(prevNode).is.emptyTextNode(true)) // Blank text nodes bypassed if set
|
7907
8000
|
) {
|
7908
8001
|
return wysihtml5.dom.domNode(prevNode).prev(options);
|
7909
8002
|
}
|
7910
|
-
|
8003
|
+
|
7911
8004
|
return prevNode;
|
7912
8005
|
},
|
7913
8006
|
|
@@ -7915,18 +8008,19 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
7915
8008
|
next: function(options) {
|
7916
8009
|
var nextNode = node.nextSibling,
|
7917
8010
|
types = (options && options.nodeTypes) ? options.nodeTypes : defaultNodeTypes;
|
7918
|
-
|
8011
|
+
|
7919
8012
|
if (!nextNode) {
|
7920
8013
|
return null;
|
7921
8014
|
}
|
7922
8015
|
|
7923
8016
|
if (
|
8017
|
+
wysihtml5.dom.domNode(nextNode).is.rangyBookmark() || // is Rangy temporary boomark element (bypass)
|
7924
8018
|
(!wysihtml5.lang.array(types).contains(nextNode.nodeType)) || // nodeTypes check.
|
7925
8019
|
(options && options.ignoreBlankTexts && wysihtml5.dom.domNode(nextNode).is.emptyTextNode(true)) // blank text nodes bypassed if set
|
7926
8020
|
) {
|
7927
8021
|
return wysihtml5.dom.domNode(nextNode).next(options);
|
7928
8022
|
}
|
7929
|
-
|
8023
|
+
|
7930
8024
|
return nextNode;
|
7931
8025
|
},
|
7932
8026
|
|
@@ -7989,7 +8083,7 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
7989
8083
|
escapeParent: function(element, newWrapper) {
|
7990
8084
|
var parent, split2, nodeWrap,
|
7991
8085
|
curNode = node;
|
7992
|
-
|
8086
|
+
|
7993
8087
|
// Stop if node is not a descendant of element
|
7994
8088
|
if (!wysihtml5.dom.contains(element, node)) {
|
7995
8089
|
throw new Error("Child is not a descendant of node.");
|
@@ -8044,12 +8138,35 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
8044
8138
|
}
|
8045
8139
|
},
|
8046
8140
|
|
8141
|
+
transferContentTo: function(targetNode, removeOldWrapper) {
|
8142
|
+
if (node.nodeType === 1) {
|
8143
|
+
if (wysihtml5.dom.domNode(targetNode).is.voidElement() || targetNode.nodeType === 3) {
|
8144
|
+
while (node.lastChild) {
|
8145
|
+
targetNode.parentNode.insertBefore(node.lastChild, targetNode.nextSibling);
|
8146
|
+
}
|
8147
|
+
} else {
|
8148
|
+
while (node.firstChild) {
|
8149
|
+
targetNode.appendChild(node.firstChild);
|
8150
|
+
}
|
8151
|
+
}
|
8152
|
+
if (removeOldWrapper) {
|
8153
|
+
node.parentNode.removeChild(node);
|
8154
|
+
}
|
8155
|
+
} else if (node.nodeType === 3 || node.nodeType === 8){
|
8156
|
+
if (wysihtml5.dom.domNode(targetNode).is.voidElement()) {
|
8157
|
+
targetNode.parentNode.insertBefore(node, targetNode.nextSibling);
|
8158
|
+
} else {
|
8159
|
+
targetNode.appendChild(node);
|
8160
|
+
}
|
8161
|
+
}
|
8162
|
+
},
|
8163
|
+
|
8047
8164
|
/*
|
8048
8165
|
Tests a node against properties, and returns true if matches.
|
8049
8166
|
Tests on principle that all properties defined must have at least one match.
|
8050
8167
|
styleValue parameter works in context of styleProperty and has no effect otherwise.
|
8051
8168
|
Returns true if element matches and false if it does not.
|
8052
|
-
|
8169
|
+
|
8053
8170
|
Properties for filtering element:
|
8054
8171
|
{
|
8055
8172
|
query: selector string,
|
@@ -8082,7 +8199,7 @@ wysihtml5.dom.copyAttributes = function(attributesToCopy) {
|
|
8082
8199
|
}
|
8083
8200
|
}
|
8084
8201
|
|
8085
|
-
if (properties.nodeName && node.nodeName !== properties.nodeName) {
|
8202
|
+
if (properties.nodeName && node.nodeName.toLowerCase() !== properties.nodeName.toLowerCase()) {
|
8086
8203
|
return false;
|
8087
8204
|
}
|
8088
8205
|
|
@@ -9492,7 +9609,7 @@ wysihtml5.dom.renameElement = function(element, newNodeName) {
|
|
9492
9609
|
newElement.appendChild(firstChild);
|
9493
9610
|
}
|
9494
9611
|
wysihtml5.dom.copyAttributes(["align", "className"]).from(element).to(newElement);
|
9495
|
-
|
9612
|
+
|
9496
9613
|
if (element.parentNode) {
|
9497
9614
|
element.parentNode.replaceChild(newElement, element);
|
9498
9615
|
}
|
@@ -9517,17 +9634,10 @@ wysihtml5.dom.replaceWithChildNodes = function(node) {
|
|
9517
9634
|
return;
|
9518
9635
|
}
|
9519
9636
|
|
9520
|
-
if (!node.firstChild) {
|
9521
|
-
node.parentNode.removeChild(node);
|
9522
|
-
return;
|
9523
|
-
}
|
9524
|
-
|
9525
|
-
var fragment = node.ownerDocument.createDocumentFragment();
|
9526
9637
|
while (node.firstChild) {
|
9527
|
-
|
9638
|
+
node.parentNode.insertBefore(node.firstChild, node);
|
9528
9639
|
}
|
9529
|
-
node.parentNode.
|
9530
|
-
node = fragment = null;
|
9640
|
+
node.parentNode.removeChild(node);
|
9531
9641
|
};
|
9532
9642
|
;/**
|
9533
9643
|
* Unwraps an unordered/ordered list
|
@@ -11116,12 +11226,12 @@ wysihtml5.dom.unwrap = function(node) {
|
|
11116
11226
|
}
|
11117
11227
|
return children;
|
11118
11228
|
};
|
11119
|
-
;/*
|
11229
|
+
;/*
|
11120
11230
|
* Methods for fetching pasted html before it gets inserted into content
|
11121
11231
|
**/
|
11122
11232
|
|
11123
11233
|
/* Modern event.clipboardData driven approach.
|
11124
|
-
* Advantage is that it does not have to loose selection or modify dom to catch the data.
|
11234
|
+
* Advantage is that it does not have to loose selection or modify dom to catch the data.
|
11125
11235
|
* IE does not support though.
|
11126
11236
|
**/
|
11127
11237
|
wysihtml5.dom.getPastedHtml = function(event) {
|
@@ -11142,7 +11252,7 @@ wysihtml5.dom.getPastedHtmlWithDiv = function (composer, f) {
|
|
11142
11252
|
doc = composer.element.ownerDocument,
|
11143
11253
|
cleanerDiv = doc.createElement('DIV'),
|
11144
11254
|
scrollPos = composer.getScrollPos();
|
11145
|
-
|
11255
|
+
|
11146
11256
|
doc.body.appendChild(cleanerDiv);
|
11147
11257
|
|
11148
11258
|
cleanerDiv.style.width = "1px";
|
@@ -11444,7 +11554,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11444
11554
|
|
11445
11555
|
};
|
11446
11556
|
;(function(wysihtml5) {
|
11447
|
-
|
11557
|
+
|
11448
11558
|
// List of supported color format parsing methods
|
11449
11559
|
// If radix is not defined 10 is expected as default
|
11450
11560
|
var colorParseMethods = {
|
@@ -11487,7 +11597,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11487
11597
|
}
|
11488
11598
|
}
|
11489
11599
|
|
11490
|
-
// Takes color string value ("#abc", "rgb(1,2,3)", ...) as an argument and returns the type of that color format "hex", "rgb", "rgba".
|
11600
|
+
// Takes color string value ("#abc", "rgb(1,2,3)", ...) as an argument and returns the type of that color format "hex", "rgb", "rgba".
|
11491
11601
|
function getColorFormat (colorStr) {
|
11492
11602
|
var type = getColorParseMethod(colorStr);
|
11493
11603
|
|
@@ -11500,9 +11610,9 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11500
11610
|
// Takes color string value as an argument and returns suitable parsing method for it
|
11501
11611
|
getColorParseMethod : getColorParseMethod,
|
11502
11612
|
|
11503
|
-
// Takes color string value as an argument and returns the type of that color format "hex", "rgb", "rgba".
|
11613
|
+
// Takes color string value as an argument and returns the type of that color format "hex", "rgb", "rgba".
|
11504
11614
|
getColorFormat : getColorFormat,
|
11505
|
-
|
11615
|
+
|
11506
11616
|
/* Parses a color string to and array of [red, green, blue, alpha].
|
11507
11617
|
* paramName: optional argument to parse color value directly from style string parameter
|
11508
11618
|
*
|
@@ -11630,6 +11740,14 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11630
11740
|
return ret;
|
11631
11741
|
}
|
11632
11742
|
|
11743
|
+
function getRangeNode(node, offset) {
|
11744
|
+
if (node.nodeType === 3) {
|
11745
|
+
return node;
|
11746
|
+
} else {
|
11747
|
+
return node.childNodes[offset] || node;
|
11748
|
+
}
|
11749
|
+
}
|
11750
|
+
|
11633
11751
|
function getWebkitSelectionFixNode(container) {
|
11634
11752
|
var blankNode = document.createElement('span');
|
11635
11753
|
|
@@ -11656,7 +11774,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11656
11774
|
}
|
11657
11775
|
};
|
11658
11776
|
|
11659
|
-
blankNode.appendChild(
|
11777
|
+
blankNode.appendChild(container.ownerDocument.createTextNode(wysihtml5.INVISIBLE_SPACE));
|
11660
11778
|
blankNode.className = '_wysihtml5-temp-caret-fix';
|
11661
11779
|
blankNode.style.display = 'block';
|
11662
11780
|
blankNode.style.minWidth = '1px';
|
@@ -11700,7 +11818,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11700
11818
|
/** @scope wysihtml5.Selection.prototype */ {
|
11701
11819
|
constructor: function(editor, contain, unselectableClass) {
|
11702
11820
|
// Make sure that our external range library is initialized
|
11703
|
-
|
11821
|
+
rangy.init();
|
11704
11822
|
|
11705
11823
|
this.editor = editor;
|
11706
11824
|
this.composer = editor.composer;
|
@@ -11833,7 +11951,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
11833
11951
|
if (!sel || (lastSibling === node && node.nodeType === 1 && win.getComputedStyle(node).display === "block")) {
|
11834
11952
|
if (notVisual) {
|
11835
11953
|
// If setAfter is used as internal between actions, self-removing caretPlaceholder has simpler implementation
|
11836
|
-
// and remove itself in call stack end instead on user interaction
|
11954
|
+
// and remove itself in call stack end instead on user interaction
|
11837
11955
|
var caretPlaceholder = this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE);
|
11838
11956
|
node.parentNode.insertBefore(caretPlaceholder, node.nextSibling);
|
11839
11957
|
this.selectNode(caretPlaceholder);
|
@@ -12000,11 +12118,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12000
12118
|
this.deleteRangeContents(range);
|
12001
12119
|
this.setSelection(range);
|
12002
12120
|
},
|
12003
|
-
|
12121
|
+
|
12004
12122
|
// Makes sure all uneditable sare notified before deleting contents
|
12005
12123
|
deleteRangeContents: function (range) {
|
12006
12124
|
var startParent, endParent, uneditables, ev;
|
12007
|
-
|
12125
|
+
|
12008
12126
|
if (this.unselectableClass) {
|
12009
12127
|
if ((startParent = wysihtml5.dom.getParentElement(range.startContainer, { query: "." + this.unselectableClass }, false, this.contain))) {
|
12010
12128
|
range.setStartBefore(startParent);
|
@@ -12027,11 +12145,16 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12027
12145
|
range.deleteContents();
|
12028
12146
|
},
|
12029
12147
|
|
12148
|
+
getCaretNode: function () {
|
12149
|
+
var selection = this.getSelection();
|
12150
|
+
return (selection && selection.anchorNode) ? getRangeNode(selection.anchorNode, selection.anchorOffset) : null;
|
12151
|
+
},
|
12152
|
+
|
12030
12153
|
getPreviousNode: function(node, ignoreEmpty) {
|
12031
12154
|
var displayStyle;
|
12032
12155
|
if (!node) {
|
12033
12156
|
var selection = this.getSelection();
|
12034
|
-
node = selection.anchorNode;
|
12157
|
+
node = (selection && selection.anchorNode) ? getRangeNode(selection.anchorNode, selection.anchorOffset) : null;
|
12035
12158
|
}
|
12036
12159
|
|
12037
12160
|
if (node === this.contain) {
|
@@ -12072,6 +12195,50 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12072
12195
|
return (ret !== this.contain) ? ret : false;
|
12073
12196
|
},
|
12074
12197
|
|
12198
|
+
// Gather info about caret location (caret node, previous and next node)
|
12199
|
+
getNodesNearCaret: function() {
|
12200
|
+
if (!this.isCollapsed()) {
|
12201
|
+
throw "Selection must be caret when using selection.getNodesNearCaret()";
|
12202
|
+
}
|
12203
|
+
|
12204
|
+
var r = this.getOwnRanges(),
|
12205
|
+
caretNode, prevNode, nextNode, offset;
|
12206
|
+
|
12207
|
+
if (r && r.length > 0) {
|
12208
|
+
if (r[0].startContainer.nodeType === 1) {
|
12209
|
+
caretNode = r[0].startContainer.childNodes[r[0].startOffset - 1];
|
12210
|
+
if (!caretNode && r[0].startOffset === 0) {
|
12211
|
+
// Is first position before all nodes
|
12212
|
+
nextNode = r[0].startContainer.childNodes[0];
|
12213
|
+
} else if (caretNode) {
|
12214
|
+
prevNode = caretNode.previousSibling;
|
12215
|
+
nextNode = caretNode.nextSibling;
|
12216
|
+
}
|
12217
|
+
} else {
|
12218
|
+
if (r[0].startOffset === 0 && r[0].startContainer.previousSibling) {
|
12219
|
+
caretNode = r[0].startContainer.previousSibling;
|
12220
|
+
if (caretNode.nodeType === 3) {
|
12221
|
+
offset = caretNode.data.length;
|
12222
|
+
}
|
12223
|
+
} else {
|
12224
|
+
caretNode = r[0].startContainer;
|
12225
|
+
offset = r[0].startOffset;
|
12226
|
+
}
|
12227
|
+
prevNode = caretNode.previousSibling;
|
12228
|
+
nextNode = caretNode.nextSibling;
|
12229
|
+
}
|
12230
|
+
|
12231
|
+
return {
|
12232
|
+
"caretNode": caretNode,
|
12233
|
+
"prevNode": prevNode,
|
12234
|
+
"nextNode": nextNode,
|
12235
|
+
"textOffset": offset
|
12236
|
+
};
|
12237
|
+
}
|
12238
|
+
|
12239
|
+
return null;
|
12240
|
+
},
|
12241
|
+
|
12075
12242
|
getSelectionParentsByTag: function(tagName) {
|
12076
12243
|
var nodes = this.getSelectedOwnNodes(),
|
12077
12244
|
curEl, parents = [];
|
@@ -12107,15 +12274,24 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12107
12274
|
return (/^\s*$/).test(endtxt);
|
12108
12275
|
},
|
12109
12276
|
|
12110
|
-
caretIsFirstInSelection: function() {
|
12277
|
+
caretIsFirstInSelection: function(includeLineBreaks) {
|
12111
12278
|
var r = rangy.createRange(this.doc),
|
12112
12279
|
s = this.getSelection(),
|
12113
12280
|
range = this.getRange(),
|
12114
|
-
startNode = range.startContainer;
|
12115
|
-
|
12281
|
+
startNode = getRangeNode(range.startContainer, range.startOffset);
|
12282
|
+
|
12116
12283
|
if (startNode) {
|
12117
12284
|
if (startNode.nodeType === wysihtml5.TEXT_NODE) {
|
12118
|
-
|
12285
|
+
if (!startNode.parentNode) {
|
12286
|
+
return false;
|
12287
|
+
}
|
12288
|
+
if (!this.isCollapsed() || (startNode.parentNode.firstChild !== startNode && !wysihtml5.dom.domNode(startNode.previousSibling).is.block())) {
|
12289
|
+
return false;
|
12290
|
+
}
|
12291
|
+
var ws = this.win.getComputedStyle(startNode.parentNode).whiteSpace;
|
12292
|
+
return (ws === "pre" || ws === "pre-wrap") ? range.startOffset === 0 : (/^\s*$/).test(startNode.data.substr(0,range.startOffset));
|
12293
|
+
} else if (includeLineBreaks && wysihtml5.dom.domNode(startNode).is.lineBreak()) {
|
12294
|
+
return true;
|
12119
12295
|
} else {
|
12120
12296
|
r.selectNodeContents(this.getRange().commonAncestorContainer);
|
12121
12297
|
r.collapse(true);
|
@@ -12143,6 +12319,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12143
12319
|
startOffset = (sel.isBackwards()) ? sel.focusOffset : sel.anchorOffset,
|
12144
12320
|
rng = this.createRange(), endNode, inTmpCaret;
|
12145
12321
|
|
12322
|
+
// If start is textnode and all is whitespace before caret. Set start offset to 0
|
12323
|
+
if (startNode && startNode.nodeType === 3 && (/^\s*$/).test(startNode.data.slice(0, startOffset))) {
|
12324
|
+
startOffset = 0;
|
12325
|
+
}
|
12326
|
+
|
12146
12327
|
// Escape temproray helper nodes if selection in them
|
12147
12328
|
inTmpCaret = wysihtml5.dom.getParentElement(startNode, { query: '._wysihtml5-temp-caret-fix' }, 1);
|
12148
12329
|
if (inTmpCaret) {
|
@@ -12323,7 +12504,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12323
12504
|
node = this.doc.createElement('DIV'),
|
12324
12505
|
fragment = this.doc.createDocumentFragment(),
|
12325
12506
|
lastChild, lastEditorElement;
|
12326
|
-
|
12507
|
+
|
12327
12508
|
if (range) {
|
12328
12509
|
range.deleteContents();
|
12329
12510
|
node.innerHTML = html;
|
@@ -12333,7 +12514,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12333
12514
|
fragment.appendChild(node.firstChild);
|
12334
12515
|
}
|
12335
12516
|
range.insertNode(fragment);
|
12336
|
-
|
12517
|
+
|
12337
12518
|
lastEditorElement = this.contain.lastChild;
|
12338
12519
|
while (lastEditorElement && lastEditorElement.nodeType === 3 && lastEditorElement.previousSibling && (/^\s*$/).test(lastEditorElement.data)) {
|
12339
12520
|
lastEditorElement = lastEditorElement.previousSibling;
|
@@ -12462,43 +12643,6 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12462
12643
|
return nodes;
|
12463
12644
|
},
|
12464
12645
|
|
12465
|
-
deblockAndSurround: function(nodeOptions) {
|
12466
|
-
var tempElement = this.doc.createElement('div'),
|
12467
|
-
range = rangy.createRange(this.doc),
|
12468
|
-
tempDivElements,
|
12469
|
-
tempElements,
|
12470
|
-
firstChild;
|
12471
|
-
|
12472
|
-
tempElement.className = nodeOptions.className;
|
12473
|
-
|
12474
|
-
this.composer.commands.exec("formatBlock", nodeOptions);
|
12475
|
-
tempDivElements = this.contain.querySelectorAll("." + nodeOptions.className);
|
12476
|
-
if (tempDivElements[0]) {
|
12477
|
-
tempDivElements[0].parentNode.insertBefore(tempElement, tempDivElements[0]);
|
12478
|
-
|
12479
|
-
range.setStartBefore(tempDivElements[0]);
|
12480
|
-
range.setEndAfter(tempDivElements[tempDivElements.length - 1]);
|
12481
|
-
tempElements = range.extractContents();
|
12482
|
-
|
12483
|
-
while (tempElements.firstChild) {
|
12484
|
-
firstChild = tempElements.firstChild;
|
12485
|
-
if (firstChild.nodeType == 1 && wysihtml5.dom.hasClass(firstChild, nodeOptions.className)) {
|
12486
|
-
while (firstChild.firstChild) {
|
12487
|
-
tempElement.appendChild(firstChild.firstChild);
|
12488
|
-
}
|
12489
|
-
if (firstChild.nodeName !== "BR") { tempElement.appendChild(this.doc.createElement('br')); }
|
12490
|
-
tempElements.removeChild(firstChild);
|
12491
|
-
} else {
|
12492
|
-
tempElement.appendChild(firstChild);
|
12493
|
-
}
|
12494
|
-
}
|
12495
|
-
} else {
|
12496
|
-
tempElement = null;
|
12497
|
-
}
|
12498
|
-
|
12499
|
-
return tempElement;
|
12500
|
-
},
|
12501
|
-
|
12502
12646
|
/**
|
12503
12647
|
* Scroll the current caret position into the view
|
12504
12648
|
* FIXME: This is a bit hacky, there might be a smarter way of doing this
|
@@ -12532,15 +12676,39 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12532
12676
|
* Select line where the caret is in
|
12533
12677
|
*/
|
12534
12678
|
selectLine: function() {
|
12679
|
+
var r = rangy.createRange();
|
12535
12680
|
if (wysihtml5.browser.supportsSelectionModify()) {
|
12536
12681
|
this._selectLine_W3C();
|
12537
|
-
} else if (
|
12538
|
-
this._selectLine_MSIE();
|
12539
|
-
} else {
|
12682
|
+
} else if (r.nativeRange && r.nativeRange.getBoundingClientRect) {
|
12540
12683
|
// For IE Edge as it ditched the old api and did not fully implement the new one (as expected)
|
12541
12684
|
this._selectLineUniversal();
|
12542
12685
|
}
|
12543
12686
|
},
|
12687
|
+
|
12688
|
+
includeRangyRangeHelpers: function() {
|
12689
|
+
var s = this.getSelection(),
|
12690
|
+
r = s.getRangeAt(0),
|
12691
|
+
isHelperNode = function(node) {
|
12692
|
+
return (node && node.nodeType === 1 && node.classList.contains('rangySelectionBoundary'));
|
12693
|
+
},
|
12694
|
+
getNodeLength = function (node) {
|
12695
|
+
if (node.nodeType === 1) {
|
12696
|
+
return node.childNodes && node.childNodes.length || 0;
|
12697
|
+
} else {
|
12698
|
+
return node.data && node.data.length || 0;
|
12699
|
+
}
|
12700
|
+
},
|
12701
|
+
anode = s.anchorNode.nodeType === 1 ? s.anchorNode.childNodes[s.anchorOffset] : s.anchorNode,
|
12702
|
+
fnode = s.focusNode.nodeType === 1 ? s.focusNode.childNodes[s.focusOffset] : s.focusNode;
|
12703
|
+
|
12704
|
+
if (fnode && s.focusOffset === getNodeLength(fnode) && fnode.nextSibling && isHelperNode(fnode.nextSibling)) {
|
12705
|
+
r.setEndAfter(fnode.nextSibling);
|
12706
|
+
}
|
12707
|
+
if (anode && s.anchorOffset === 0 && anode.previousSibling && isHelperNode(anode.previousSibling)) {
|
12708
|
+
r.setStartBefore(anode.previousSibling);
|
12709
|
+
}
|
12710
|
+
r.select();
|
12711
|
+
},
|
12544
12712
|
|
12545
12713
|
/**
|
12546
12714
|
* See https://developer.mozilla.org/en/DOM/Selection/modify
|
@@ -12548,10 +12716,10 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12548
12716
|
_selectLine_W3C: function() {
|
12549
12717
|
var selection = this.win.getSelection(),
|
12550
12718
|
initialBoundry = [selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset];
|
12551
|
-
|
12719
|
+
|
12552
12720
|
selection.modify("move", "left", "lineboundary");
|
12553
12721
|
selection.modify("extend", "right", "lineboundary");
|
12554
|
-
|
12722
|
+
|
12555
12723
|
// IF lineboundary extending did not change selection try universal fallback (FF fails sometimes without a reason)
|
12556
12724
|
if (selection.anchorNode === initialBoundry[0] &&
|
12557
12725
|
selection.anchorOffset === initialBoundry[1] &&
|
@@ -12559,6 +12727,8 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12559
12727
|
selection.focusOffset === initialBoundry[3]
|
12560
12728
|
) {
|
12561
12729
|
this._selectLineUniversal();
|
12730
|
+
} else {
|
12731
|
+
this.includeRangyRangeHelpers();
|
12562
12732
|
}
|
12563
12733
|
},
|
12564
12734
|
|
@@ -12610,89 +12780,88 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12610
12780
|
rect,
|
12611
12781
|
startRange, endRange, testRange,
|
12612
12782
|
count = 0,
|
12613
|
-
amount, testRect, found
|
12783
|
+
amount, testRect, found,
|
12784
|
+
that = this,
|
12785
|
+
isLineBreakingElement = function(el) {
|
12786
|
+
return el && el.nodeType === 1 && (that.win.getComputedStyle(el).display === "block" || wysihtml5.lang.array(['BR', 'HR']).contains(el.nodeName));
|
12787
|
+
},
|
12788
|
+
prevNode = function(node) {
|
12789
|
+
var pnode = node;
|
12790
|
+
if (pnode) {
|
12791
|
+
while (pnode && ((pnode.nodeType === 1 && pnode.classList.contains('rangySelectionBoundary')) || (pnode.nodeType === 3 && (/^\s*$/).test(pnode.data)))) {
|
12792
|
+
pnode = pnode.previousSibling;
|
12793
|
+
}
|
12794
|
+
}
|
12795
|
+
return pnode;
|
12796
|
+
};
|
12614
12797
|
|
12615
12798
|
startRange = r.cloneRange();
|
12616
12799
|
endRange = r.cloneRange();
|
12617
12800
|
|
12618
12801
|
if (r.collapsed) {
|
12619
|
-
|
12620
|
-
|
12802
|
+
// Collapsed state can not have a bounding rect. Thus need to expand it at least by 1 character first while not crossing line boundary
|
12803
|
+
// TODO: figure out a shorter and more readable way
|
12804
|
+
if (r.startContainer.nodeType === 3 && r.startOffset < r.startContainer.data.length) {
|
12805
|
+
r.moveEnd('character', 1);
|
12806
|
+
} else if (r.startContainer.nodeType === 1 && r.startContainer.childNodes[r.startOffset] && r.startContainer.childNodes[r.startOffset].nodeType === 3 && r.startContainer.childNodes[r.startOffset].data.length > 0) {
|
12807
|
+
r.moveEnd('character', 1);
|
12808
|
+
} else if (
|
12809
|
+
r.startOffset > 0 &&
|
12810
|
+
(
|
12811
|
+
r.startContainer.nodeType === 3 ||
|
12812
|
+
(
|
12813
|
+
r.startContainer.nodeType === 1 &&
|
12814
|
+
!isLineBreakingElement(prevNode(r.startContainer.childNodes[r.startOffset - 1]))
|
12815
|
+
)
|
12816
|
+
)
|
12817
|
+
) {
|
12818
|
+
r.moveStart('character', -1);
|
12819
|
+
}
|
12621
12820
|
}
|
12622
|
-
|
12821
|
+
if (!r.collapsed) {
|
12822
|
+
r.insertNode(this.doc.createTextNode(wysihtml5.INVISIBLE_SPACE));
|
12823
|
+
}
|
12824
|
+
|
12825
|
+
// Is probably just empty line as can not be expanded
|
12826
|
+
rect = r.nativeRange.getBoundingClientRect();
|
12827
|
+
// If startnode is not line break allready move the start position of range by -1 character until clientRect top changes;
|
12623
12828
|
do {
|
12624
12829
|
amount = r.moveStart('character', -1);
|
12625
12830
|
testRect = r.nativeRange.getBoundingClientRect();
|
12831
|
+
|
12626
12832
|
if (!testRect || Math.floor(testRect.top) !== Math.floor(rect.top)) {
|
12627
12833
|
r.moveStart('character', 1);
|
12628
12834
|
found = true;
|
12629
12835
|
}
|
12630
12836
|
count++;
|
12631
12837
|
} while (amount !== 0 && !found && count < 2000);
|
12632
|
-
|
12633
12838
|
count = 0;
|
12634
12839
|
found = false;
|
12635
12840
|
rect = r.nativeRange.getBoundingClientRect();
|
12636
|
-
|
12637
|
-
|
12638
|
-
|
12639
|
-
|
12640
|
-
r.
|
12641
|
-
|
12642
|
-
|
12643
|
-
|
12644
|
-
|
12645
|
-
|
12646
|
-
|
12647
|
-
|
12648
|
-
|
12649
|
-
|
12650
|
-
|
12651
|
-
|
12652
|
-
|
12653
|
-
|
12654
|
-
|
12655
|
-
|
12656
|
-
|
12657
|
-
j;
|
12658
|
-
|
12659
|
-
window.r = range;
|
12660
|
-
|
12661
|
-
if (!range.moveToPoint) {
|
12662
|
-
return;
|
12663
|
-
}
|
12664
|
-
|
12665
|
-
if (rangeTop === 0) {
|
12666
|
-
// Don't know why, but when the selection ends at the end of a line
|
12667
|
-
// range.boundingTop is 0
|
12668
|
-
measureNode = this.doc.createElement("span");
|
12669
|
-
this.insertNode(measureNode);
|
12670
|
-
rangeTop = measureNode.offsetTop;
|
12671
|
-
measureNode.parentNode.removeChild(measureNode);
|
12672
|
-
}
|
12673
|
-
|
12674
|
-
rangeTop += 1;
|
12675
|
-
|
12676
|
-
for (i=-10; i<scrollWidth; i+=2) {
|
12677
|
-
try {
|
12678
|
-
range.moveToPoint(i, rangeTop);
|
12679
|
-
break;
|
12680
|
-
} catch(e1) {}
|
12681
|
-
}
|
12682
|
-
|
12683
|
-
// Investigate the following in order to handle multi line selections
|
12684
|
-
// rangeBottom = rangeTop + (rangeHeight ? (rangeHeight - 1) : 0);
|
12685
|
-
rangeBottom = rangeTop;
|
12686
|
-
rangeEnd = this.doc.selection.createRange();
|
12687
|
-
for (j=scrollWidth; j>=0; j--) {
|
12688
|
-
try {
|
12689
|
-
rangeEnd.moveToPoint(j, rangeBottom);
|
12690
|
-
break;
|
12691
|
-
} catch(e2) {}
|
12841
|
+
|
12842
|
+
if (r.endContainer !== this.contain || (this.contain.lastChild && this.contain.childNodes[r.endOffset] !== this.contain.lastChild)) {
|
12843
|
+
do {
|
12844
|
+
amount = r.moveEnd('character', 1);
|
12845
|
+
testRect = r.nativeRange.getBoundingClientRect();
|
12846
|
+
if (!testRect || Math.floor(testRect.bottom) !== Math.floor(rect.bottom)) {
|
12847
|
+
r.moveEnd('character', -1);
|
12848
|
+
|
12849
|
+
// Fix a IE line end marked by linebreak element although caret is before it
|
12850
|
+
// If causes problems should be changed to be applied only to IE
|
12851
|
+
if (r.endContainer && r.endContainer.nodeType === 1 && r.endContainer.childNodes[r.endOffset] && r.endContainer.childNodes[r.endOffset].nodeType === 1 && r.endContainer.childNodes[r.endOffset].nodeName === "BR" && r.endContainer.childNodes[r.endOffset].previousSibling) {
|
12852
|
+
if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 1) {
|
12853
|
+
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.childNodes.length);
|
12854
|
+
} else if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 3) {
|
12855
|
+
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.data.length);
|
12856
|
+
}
|
12857
|
+
}
|
12858
|
+
found = true;
|
12859
|
+
}
|
12860
|
+
count++;
|
12861
|
+
} while (amount !== 0 && !found && count < 2000);
|
12692
12862
|
}
|
12693
|
-
|
12694
|
-
|
12695
|
-
range.select();
|
12863
|
+
r.select();
|
12864
|
+
this.includeRangyRangeHelpers();
|
12696
12865
|
},
|
12697
12866
|
|
12698
12867
|
getText: function() {
|
@@ -12863,7 +13032,7 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12863
13032
|
|
12864
13033
|
wysihtml5.dom.removeInvisibleSpaces(this.composer.element);
|
12865
13034
|
doSelect();
|
12866
|
-
|
13035
|
+
|
12867
13036
|
if (this.composer.element.firstChild && notSelected()) {
|
12868
13037
|
// Try fixing end
|
12869
13038
|
this.composer.element.appendChild(blankEndNode);
|
@@ -12872,11 +13041,11 @@ wysihtml5.quirks.ensureProperClearing = (function() {
|
|
12872
13041
|
if (notSelected()) {
|
12873
13042
|
// Remove end fix
|
12874
13043
|
blankEndNode.parentNode.removeChild(blankEndNode);
|
12875
|
-
|
13044
|
+
|
12876
13045
|
// Try fixing beginning
|
12877
13046
|
this.composer.element.insertBefore(blankStartNode, this.composer.element.firstChild);
|
12878
13047
|
doSelect();
|
12879
|
-
|
13048
|
+
|
12880
13049
|
if (notSelected()) {
|
12881
13050
|
// Try fixing both
|
12882
13051
|
this.composer.element.appendChild(blankEndNode);
|
@@ -13654,7 +13823,7 @@ wysihtml5.Commands = Base.extend(
|
|
13654
13823
|
result = null;
|
13655
13824
|
|
13656
13825
|
// If composer ahs placeholder unset it before command
|
13657
|
-
// Do not apply on commands that are behavioral
|
13826
|
+
// Do not apply on commands that are behavioral
|
13658
13827
|
if (this.composer.hasPlaceholderSet() && !wysihtml5.lang.array(['styleWithCSS', 'enableObjectResizing', 'enableInlineTableEditing']).contains(command)) {
|
13659
13828
|
this.composer.element.innerHTML = "";
|
13660
13829
|
this.composer.selection.selectNode(this.composer.element);
|
@@ -13727,12 +13896,12 @@ wysihtml5.Commands = Base.extend(
|
|
13727
13896
|
}
|
13728
13897
|
});
|
13729
13898
|
;(function(wysihtml5) {
|
13730
|
-
|
13899
|
+
|
13731
13900
|
var nodeOptions = {
|
13732
13901
|
nodeName: "B",
|
13733
13902
|
toggle: true
|
13734
13903
|
};
|
13735
|
-
|
13904
|
+
|
13736
13905
|
wysihtml5.commands.bold = {
|
13737
13906
|
exec: function(composer, command) {
|
13738
13907
|
wysihtml5.commands.formatInline.exec(composer, command, nodeOptions);
|
@@ -13971,9 +14140,9 @@ wysihtml5.Commands = Base.extend(
|
|
13971
14140
|
};
|
13972
14141
|
})(wysihtml5);
|
13973
14142
|
;/* Formatblock
|
13974
|
-
* Is used to insert block level elements
|
14143
|
+
* Is used to insert block level elements
|
13975
14144
|
* It tries to solve the case that some block elements should not contain other block level elements (h1-6, p, ...)
|
13976
|
-
*
|
14145
|
+
*
|
13977
14146
|
*/
|
13978
14147
|
(function(wysihtml5) {
|
13979
14148
|
|
@@ -13993,18 +14162,56 @@ wysihtml5.Commands = Base.extend(
|
|
13993
14162
|
};
|
13994
14163
|
}
|
13995
14164
|
|
14165
|
+
function getRangeNode(node, offset) {
|
14166
|
+
if (node.nodeType === 3) {
|
14167
|
+
return node;
|
14168
|
+
} else {
|
14169
|
+
return node.childNodes[offset] || node;
|
14170
|
+
}
|
14171
|
+
}
|
14172
|
+
|
14173
|
+
// Returns if node is a line break
|
14174
|
+
function isBr(n) {
|
14175
|
+
return n && n.nodeType === 1 && n.nodeName === "BR";
|
14176
|
+
}
|
14177
|
+
|
14178
|
+
// Is block level element
|
14179
|
+
function isBlock(n, composer) {
|
14180
|
+
return n && n.nodeType === 1 && composer.win.getComputedStyle(n).display === "block";
|
14181
|
+
}
|
14182
|
+
|
14183
|
+
// Returns if node is the rangy selection bookmark element (that must not be taken into account in most situatons and is removed on selection restoring)
|
14184
|
+
function isBookmark(n) {
|
14185
|
+
return n && n.nodeType === 1 && n.classList.contains('rangySelectionBoundary');
|
14186
|
+
}
|
14187
|
+
|
14188
|
+
// Is line breaking node
|
14189
|
+
function isLineBreaking(n, composer) {
|
14190
|
+
return isBr(n) || isBlock(n, composer);
|
14191
|
+
}
|
14192
|
+
|
13996
14193
|
// Removes empty block level elements
|
13997
|
-
function cleanup(composer) {
|
14194
|
+
function cleanup(composer, newBlockElements) {
|
14195
|
+
wysihtml5.dom.removeInvisibleSpaces(composer.element);
|
13998
14196
|
var container = composer.element,
|
13999
14197
|
allElements = container.querySelectorAll(BLOCK_ELEMENTS),
|
14000
|
-
|
14001
|
-
|
14198
|
+
noEditQuery = composer.config.classNames.uneditableContainer + ([""]).concat(BLOCK_ELEMENTS.split(',')).join(", " + composer.config.classNames.uneditableContainer + ' '),
|
14199
|
+
uneditables = container.querySelectorAll(noEditQuery),
|
14200
|
+
elements = wysihtml5.lang.array(allElements).without(uneditables), // Lets not touch uneditable elements and their contents
|
14201
|
+
nbIdx;
|
14002
14202
|
|
14003
14203
|
for (var i = elements.length; i--;) {
|
14004
|
-
if (elements[i].innerHTML.replace(/[\uFEFF]/g, '') === "") {
|
14204
|
+
if (elements[i].innerHTML.replace(/[\uFEFF]/g, '') === "" && (newBlockElements.length === 0 || elements[i] !== newBlockElements[newBlockElements.length - 1])) {
|
14205
|
+
// If cleanup removes some new block elements. remove them from newblocks array too
|
14206
|
+
nbIdx = wysihtml5.lang.array(newBlockElements).indexOf(elements[i]);
|
14207
|
+
if (nbIdx > -1) {
|
14208
|
+
newBlockElements.splice(nbIdx, 1);
|
14209
|
+
}
|
14005
14210
|
elements[i].parentNode.removeChild(elements[i]);
|
14006
14211
|
}
|
14007
14212
|
}
|
14213
|
+
|
14214
|
+
return newBlockElements;
|
14008
14215
|
}
|
14009
14216
|
|
14010
14217
|
function defaultNodeName(composer) {
|
@@ -14015,7 +14222,7 @@ wysihtml5.Commands = Base.extend(
|
|
14015
14222
|
function findOuterBlock(node, container, allBlocks) {
|
14016
14223
|
var n = node,
|
14017
14224
|
block = null;
|
14018
|
-
|
14225
|
+
|
14019
14226
|
while (n && container && n !== container) {
|
14020
14227
|
if (n.nodeType === 1 && n.matches(allBlocks ? BLOCK_ELEMENTS : UNNESTABLE_BLOCK_ELEMENTS)) {
|
14021
14228
|
block = n;
|
@@ -14026,6 +14233,8 @@ wysihtml5.Commands = Base.extend(
|
|
14026
14233
|
return block;
|
14027
14234
|
}
|
14028
14235
|
|
14236
|
+
// Clone for splitting the inner inline element out of its parent inline elements context
|
14237
|
+
// For example if selection is in bold and italic, clone the outer nodes and wrap these around content and return
|
14029
14238
|
function cloneOuterInlines(node, container) {
|
14030
14239
|
var n = node,
|
14031
14240
|
innerNode,
|
@@ -14088,7 +14297,10 @@ wysihtml5.Commands = Base.extend(
|
|
14088
14297
|
// Unsets element properties by options
|
14089
14298
|
// If nodename given and matches current element, element is unwrapped or converted to default node (depending on presence of class and style attributes)
|
14090
14299
|
function removeOptionsFromElement(element, options, composer) {
|
14091
|
-
var style, classes
|
14300
|
+
var style, classes,
|
14301
|
+
prevNode = element.previousSibling,
|
14302
|
+
nextNode = element.nextSibling,
|
14303
|
+
unwrapped = false;
|
14092
14304
|
|
14093
14305
|
if (options.styleProperty) {
|
14094
14306
|
element.style[wysihtml5.browser.fixStyleKey(options.styleProperty)] = '';
|
@@ -14106,10 +14318,11 @@ wysihtml5.Commands = Base.extend(
|
|
14106
14318
|
element.removeAttribute('class');
|
14107
14319
|
}
|
14108
14320
|
|
14109
|
-
if (options.nodeName && element.nodeName === options.nodeName) {
|
14321
|
+
if (options.nodeName && element.nodeName.toLowerCase() === options.nodeName.toLowerCase()) {
|
14110
14322
|
style = element.getAttribute('style');
|
14111
14323
|
if (!style || style.trim() === '') {
|
14112
14324
|
dom.unwrap(element);
|
14325
|
+
unwrapped = true;
|
14113
14326
|
} else {
|
14114
14327
|
element = dom.renameElement(element, defaultNodeName(composer));
|
14115
14328
|
}
|
@@ -14119,60 +14332,79 @@ wysihtml5.Commands = Base.extend(
|
|
14119
14332
|
if (element.getAttribute('style') !== null && element.getAttribute('style').trim() === "") {
|
14120
14333
|
element.removeAttribute('style');
|
14121
14334
|
}
|
14335
|
+
|
14336
|
+
if (unwrapped) {
|
14337
|
+
applySurroundingLineBreaks(prevNode, nextNode, composer);
|
14338
|
+
}
|
14122
14339
|
}
|
14123
14340
|
|
14124
14341
|
// Unwraps block level elements from inside content
|
14125
14342
|
// Useful as not all block level elements can contain other block-levels
|
14126
14343
|
function unwrapBlocksFromContent(element) {
|
14127
|
-
var
|
14344
|
+
var blocks = element.querySelectorAll(BLOCK_ELEMENTS) || [], // Find unnestable block elements in extracted contents
|
14345
|
+
nextEl, prevEl;
|
14128
14346
|
|
14129
|
-
for (var i =
|
14130
|
-
|
14131
|
-
|
14132
|
-
|
14347
|
+
for (var i = blocks.length; i--;) {
|
14348
|
+
nextEl = wysihtml5.dom.domNode(blocks[i]).next({nodeTypes: [1,3], ignoreBlankTexts: true}),
|
14349
|
+
prevEl = wysihtml5.dom.domNode(blocks[i]).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
14350
|
+
|
14351
|
+
if (nextEl && nextEl.nodeType !== 1 && nextEl.nodeName !== 'BR') {
|
14352
|
+
if ((blocks[i].innerHTML || blocks[i].nodeValue || '').trim() !== '') {
|
14353
|
+
blocks[i].parentNode.insertBefore(blocks[i].ownerDocument.createElement('BR'), nextEl);
|
14133
14354
|
}
|
14134
14355
|
}
|
14135
|
-
|
14356
|
+
if (nextEl && nextEl.nodeType !== 1 && nextEl.nodeName !== 'BR') {
|
14357
|
+
if ((blocks[i].innerHTML || blocks[i].nodeValue || '').trim() !== '') {
|
14358
|
+
blocks[i].parentNode.insertBefore(blocks[i].ownerDocument.createElement('BR'), nextEl);
|
14359
|
+
}
|
14360
|
+
}
|
14361
|
+
wysihtml5.dom.unwrap(blocks[i]);
|
14136
14362
|
}
|
14137
14363
|
}
|
14138
14364
|
|
14139
14365
|
// Fix ranges that visually cover whole block element to actually cover the block
|
14140
14366
|
function fixRangeCoverage(range, composer) {
|
14141
|
-
var node
|
14367
|
+
var node,
|
14368
|
+
start = range.startContainer,
|
14369
|
+
end = range.endContainer;
|
14142
14370
|
|
14143
|
-
|
14144
|
-
|
14145
|
-
|
14146
|
-
|
14147
|
-
|
14371
|
+
// If range has only one childNode and it is end to end the range, extend the range to contain the container element too
|
14372
|
+
// This ensures the wrapper node is modified and optios added to it
|
14373
|
+
if (start && start.nodeType === 1 && start === end) {
|
14374
|
+
if (start.firstChild === start.lastChild && range.endOffset === 1) {
|
14375
|
+
if (start !== composer.element && start.nodeName !== 'LI' && start.nodeName !== 'TD') {
|
14376
|
+
range.setStartBefore(start);
|
14377
|
+
range.setEndAfter(end);
|
14148
14378
|
}
|
14149
14379
|
}
|
14150
14380
|
return;
|
14151
14381
|
}
|
14152
14382
|
|
14153
|
-
|
14154
|
-
|
14155
|
-
|
14156
|
-
|
14383
|
+
// If range starts outside of node and ends inside at textrange and covers the whole node visually, extend end to cover the node end too
|
14384
|
+
if (start && start.nodeType === 1 && end.nodeType === 3) {
|
14385
|
+
if (start.firstChild === end && range.endOffset === end.data.length) {
|
14386
|
+
if (start !== composer.element && start.nodeName !== 'LI' && start.nodeName !== 'TD') {
|
14387
|
+
range.setEndAfter(start);
|
14157
14388
|
}
|
14158
14389
|
}
|
14159
14390
|
return;
|
14160
14391
|
}
|
14161
|
-
|
14162
|
-
|
14163
|
-
|
14164
|
-
|
14165
|
-
|
14392
|
+
|
14393
|
+
// If range ends outside of node and starts inside at textrange and covers the whole node visually, extend start to cover the node start too
|
14394
|
+
if (end && end.nodeType === 1 && start.nodeType === 3) {
|
14395
|
+
if (end.firstChild === start && range.startOffset === 0) {
|
14396
|
+
if (end !== composer.element && end.nodeName !== 'LI' && end.nodeName !== 'TD') {
|
14397
|
+
range.setStartBefore(end);
|
14166
14398
|
}
|
14167
14399
|
}
|
14168
14400
|
return;
|
14169
14401
|
}
|
14170
14402
|
|
14171
|
-
|
14172
|
-
if (
|
14173
|
-
if (range.
|
14174
|
-
node =
|
14175
|
-
if (node !== composer.element) {
|
14403
|
+
// If range covers a whole textnode and the textnode is the only child of node, extend range to node
|
14404
|
+
if (start && start.nodeType === 3 && start === end && start.parentNode.childNodes.length === 1) {
|
14405
|
+
if (range.endOffset == end.data.length && range.startOffset === 0) {
|
14406
|
+
node = start.parentNode;
|
14407
|
+
if (node !== composer.element && node.nodeName !== 'LI' && node.nodeName !== 'TD') {
|
14176
14408
|
range.setStartBefore(node);
|
14177
14409
|
range.setEndAfter(node);
|
14178
14410
|
}
|
@@ -14180,94 +14412,123 @@ wysihtml5.Commands = Base.extend(
|
|
14180
14412
|
return;
|
14181
14413
|
}
|
14182
14414
|
}
|
14183
|
-
|
14184
|
-
//
|
14185
|
-
//
|
14186
|
-
|
14187
|
-
|
14188
|
-
|
14189
|
-
|
14190
|
-
|
14191
|
-
|
14192
|
-
|
14193
|
-
|
14194
|
-
|
14195
|
-
|
14196
|
-
|
14197
|
-
|
14198
|
-
|
14199
|
-
|
14200
|
-
|
14201
|
-
|
14202
|
-
|
14203
|
-
|
14204
|
-
|
14205
|
-
|
14206
|
-
|
14207
|
-
|
14208
|
-
|
14209
|
-
|
14210
|
-
|
14211
|
-
|
14415
|
+
|
14416
|
+
// Scans ranges array for insertion points that are not allowed to insert block tags fixes/splits illegal ranges
|
14417
|
+
// Some places do not allow block level elements inbetween (inside ul and outside li)
|
14418
|
+
// TODO: might need extending for other nodes besides li (maybe dd,dl,dt)
|
14419
|
+
function fixNotPermittedInsertionPoints(ranges) {
|
14420
|
+
var newRanges = [],
|
14421
|
+
lis, j, maxj, tmpRange, rangePos, closestLI;
|
14422
|
+
|
14423
|
+
for (var i = 0, maxi = ranges.length; i < maxi; i++) {
|
14424
|
+
|
14425
|
+
// Fixes range start and end positions if inside UL or OL element (outside of LI)
|
14426
|
+
if (ranges[i].startContainer.nodeType === 1 && ranges[i].startContainer.matches('ul, ol')) {
|
14427
|
+
ranges[i].setStart(ranges[i].startContainer.childNodes[ranges[i].startOffset], 0);
|
14428
|
+
}
|
14429
|
+
if (ranges[i].endContainer.nodeType === 1 && ranges[i].endContainer.matches('ul, ol')) {
|
14430
|
+
closestLI = ranges[i].endContainer.childNodes[Math.max(ranges[i].endOffset - 1, 0)];
|
14431
|
+
if (closestLI.childNodes) {
|
14432
|
+
ranges[i].setEnd(closestLI, closestLI.childNodes.length);
|
14433
|
+
}
|
14434
|
+
}
|
14435
|
+
|
14436
|
+
// Get all LI eleemnts in selection (fully or partially covered)
|
14437
|
+
// And make sure ranges are either inside LI or outside UL/OL
|
14438
|
+
// Split and add new ranges as needed to cover same range content
|
14439
|
+
// TODO: Needs improvement to accept DL, DD, DT
|
14440
|
+
lis = ranges[i].getNodes([1], function(node) {
|
14441
|
+
return node.nodeName === "LI";
|
14442
|
+
});
|
14443
|
+
if (lis.length > 0) {
|
14444
|
+
|
14445
|
+
for (j = 0, maxj = lis.length; j < maxj; j++) {
|
14446
|
+
rangePos = ranges[i].compareNode(lis[j]);
|
14447
|
+
|
14448
|
+
// Fixes start of range that crosses LI border
|
14449
|
+
if (rangePos === ranges[i].NODE_AFTER || rangePos === ranges[i].NODE_INSIDE) {
|
14450
|
+
// Range starts before and ends inside the node
|
14451
|
+
|
14452
|
+
tmpRange = ranges[i].cloneRange();
|
14453
|
+
closestLI = wysihtml5.dom.domNode(lis[j]).prev({nodeTypes: [1]});
|
14454
|
+
|
14455
|
+
if (closestLI) {
|
14456
|
+
tmpRange.setEnd(closestLI, closestLI.childNodes.length);
|
14457
|
+
} else if (lis[j].closest('ul, ol')) {
|
14458
|
+
tmpRange.setEndBefore(lis[j].closest('ul, ol'));
|
14459
|
+
} else {
|
14460
|
+
tmpRange.setEndBefore(lis[j]);
|
14461
|
+
}
|
14462
|
+
newRanges.push(tmpRange);
|
14463
|
+
ranges[i].setStart(lis[j], 0);
|
14464
|
+
}
|
14465
|
+
|
14466
|
+
// Fixes end of range that crosses li border
|
14467
|
+
if (rangePos === ranges[i].NODE_BEFORE || rangePos === ranges[i].NODE_INSIDE) {
|
14468
|
+
// Range starts inside the node and ends after node
|
14469
|
+
|
14470
|
+
tmpRange = ranges[i].cloneRange();
|
14471
|
+
tmpRange.setEnd(lis[j], lis[j].childNodes.length);
|
14472
|
+
newRanges.push(tmpRange);
|
14473
|
+
|
14474
|
+
// Find next LI in list and if present set range to it, else
|
14475
|
+
closestLI = wysihtml5.dom.domNode(lis[j]).next({nodeTypes: [1]});
|
14476
|
+
if (closestLI) {
|
14477
|
+
ranges[i].setStart(closestLI, 0);
|
14478
|
+
} else if (lis[j].closest('ul, ol')) {
|
14479
|
+
ranges[i].setStartAfter(lis[j].closest('ul, ol'));
|
14480
|
+
} else {
|
14481
|
+
ranges[i].setStartAfter(lis[j]);
|
14482
|
+
}
|
14483
|
+
}
|
14484
|
+
}
|
14485
|
+
newRanges.push(ranges[i]);
|
14212
14486
|
} else {
|
14487
|
+
newRanges.push(ranges[i]);
|
14488
|
+
}
|
14489
|
+
}
|
14490
|
+
return newRanges;
|
14491
|
+
}
|
14492
|
+
|
14493
|
+
// Return options object with nodeName set if original did not have any
|
14494
|
+
// Node name is set to local or global default
|
14495
|
+
function getOptionsWithNodename(options, defaultName, composer) {
|
14496
|
+
var correctedOptions = (options) ? wysihtml5.lang.object(options).clone(true) : null;
|
14497
|
+
if (correctedOptions) {
|
14498
|
+
correctedOptions.nodeName = correctedOptions.nodeName || defaultName || defaultNodeName(composer);
|
14499
|
+
}
|
14500
|
+
return correctedOptions;
|
14501
|
+
}
|
14502
|
+
|
14503
|
+
// Injects document fragment to range ensuring outer elements are split to a place where block elements are allowed to be inserted
|
14504
|
+
// Also wraps empty clones of split parent tags around fragment to keep formatting
|
14505
|
+
// If firstOuterBlock is given assume that instead of finding outer (useful for solving cases of some blocks are allowed into others while others are not)
|
14506
|
+
function injectFragmentToRange(fragment, range, composer, firstOuterBlock) {
|
14507
|
+
var rangeStartContainer = range.startContainer,
|
14508
|
+
firstOuterBlock = firstOuterBlock || findOuterBlock(rangeStartContainer, composer.element, true),
|
14509
|
+
outerInlines, first, last, prev, next;
|
14510
|
+
|
14511
|
+
if (firstOuterBlock) {
|
14512
|
+
// If selection starts inside un-nestable block, split-escape the unnestable point and insert node between
|
14513
|
+
first = fragment.firstChild;
|
14514
|
+
last = fragment.lastChild;
|
14213
14515
|
|
14214
|
-
|
14516
|
+
composer.selection.splitElementAtCaret(firstOuterBlock, fragment);
|
14215
14517
|
|
14216
|
-
|
14518
|
+
next = wysihtml5.dom.domNode(last).next({nodeTypes: [1,3], ignoreBlankTexts: true});
|
14519
|
+
prev = wysihtml5.dom.domNode(first).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
14217
14520
|
|
14218
|
-
|
14219
|
-
|
14220
|
-
|
14221
|
-
if (content.firstChild.matches(UNNESTABLE_BLOCK_ELEMENTS)) {
|
14222
|
-
unwrapBlocksFromContent(content.firstChild);
|
14223
|
-
}
|
14224
|
-
fragment.appendChild(content.firstChild);
|
14521
|
+
if (first && !isLineBreaking(first, composer) && prev && !isLineBreaking(prev, composer)) {
|
14522
|
+
first.parentNode.insertBefore(composer.doc.createElement('br'), first);
|
14523
|
+
}
|
14225
14524
|
|
14226
|
-
|
14227
|
-
|
14228
|
-
|
14229
|
-
children = wysihtml5.dom.unwrap(content.firstChild);
|
14230
|
-
for (var c = 0, cmax = children.length; c < cmax; c++) {
|
14231
|
-
fragment.appendChild(children[c]);
|
14232
|
-
}
|
14525
|
+
if (last && !isLineBreaking(last, composer) && next && !isLineBreaking(next, composer)) {
|
14526
|
+
next.parentNode.insertBefore(composer.doc.createElement('br'), next);
|
14527
|
+
}
|
14233
14528
|
|
14234
|
-
if (fragment.childNodes.length > 0) {
|
14235
|
-
fragment.appendChild(composer.doc.createElement('BR'));
|
14236
|
-
}
|
14237
|
-
}
|
14238
|
-
} else {
|
14239
|
-
|
14240
|
-
if (options) {
|
14241
|
-
// Wrap subsequent non-block nodes inside new block element
|
14242
|
-
wrapper = applyOptionsToElement(null, defaultOptions, composer);
|
14243
|
-
while(content.firstChild && (content.firstChild.nodeType !== 1 || !content.firstChild.matches(BLOCK_ELEMENTS))) {
|
14244
|
-
if (content.firstChild.nodeType == 1 && wrapper.matches(UNNESTABLE_BLOCK_ELEMENTS)) {
|
14245
|
-
unwrapBlocksFromContent(content.firstChild);
|
14246
|
-
}
|
14247
|
-
wrapper.appendChild(content.firstChild);
|
14248
|
-
}
|
14249
|
-
fragment.appendChild(wrapper);
|
14250
|
-
|
14251
|
-
} else {
|
14252
|
-
// Escape(split) block formatting at selection
|
14253
|
-
if (content.firstChild.nodeType == 1) {
|
14254
|
-
unwrapBlocksFromContent(content.firstChild);
|
14255
|
-
}
|
14256
|
-
fragment.appendChild(content.firstChild);
|
14257
|
-
}
|
14258
|
-
|
14259
|
-
}
|
14260
|
-
}
|
14261
|
-
}
|
14262
|
-
|
14263
|
-
blocks = wysihtml5.lang.array(fragment.childNodes).get();
|
14264
|
-
}
|
14265
|
-
if (firstOuterBlock) {
|
14266
|
-
// If selection starts inside un-nestable block, split-escape the unnestable point and insert node between
|
14267
|
-
composer.selection.splitElementAtCaret(firstOuterBlock, fragment);
|
14268
14529
|
} else {
|
14269
14530
|
// Ensure node does not get inserted into an inline where it is not allowed
|
14270
|
-
|
14531
|
+
outerInlines = cloneOuterInlines(rangeStartContainer, composer.element);
|
14271
14532
|
if (outerInlines.outerNode && outerInlines.innerNode && outerInlines.parent) {
|
14272
14533
|
if (fragment.childNodes.length === 1) {
|
14273
14534
|
while(fragment.firstChild.firstChild) {
|
@@ -14277,11 +14538,228 @@ wysihtml5.Commands = Base.extend(
|
|
14277
14538
|
}
|
14278
14539
|
composer.selection.splitElementAtCaret(outerInlines.parent, fragment);
|
14279
14540
|
} else {
|
14280
|
-
|
14281
|
-
|
14541
|
+
var fc = fragment.firstChild,
|
14542
|
+
lc = fragment.lastChild;
|
14543
|
+
|
14544
|
+
range.insertNode(fragment);
|
14545
|
+
// restore range position as it might get lost in webkit sometimes
|
14546
|
+
range.setStartBefore(fc);
|
14547
|
+
range.setEndAfter(lc);
|
14548
|
+
}
|
14549
|
+
}
|
14550
|
+
}
|
14551
|
+
|
14552
|
+
// Removes all block formatting from range
|
14553
|
+
function clearRangeBlockFromating(range, closestBlockName, composer) {
|
14554
|
+
var r = range.cloneRange(),
|
14555
|
+
prevNode = getRangeNode(r.startContainer, r.startOffset).previousSibling,
|
14556
|
+
nextNode = getRangeNode(r.endContainer, r.endOffset).nextSibling,
|
14557
|
+
content = r.extractContents(),
|
14558
|
+
fragment = composer.doc.createDocumentFragment(),
|
14559
|
+
children, blocks,
|
14560
|
+
first = true;
|
14561
|
+
|
14562
|
+
while(content.firstChild) {
|
14563
|
+
// Iterate over all selection content first level childNodes
|
14564
|
+
if (content.firstChild.nodeType === 1 && content.firstChild.matches(BLOCK_ELEMENTS)) {
|
14565
|
+
// If node is a block element
|
14566
|
+
// Split block formating and add new block to wrap caret
|
14567
|
+
|
14568
|
+
unwrapBlocksFromContent(content.firstChild);
|
14569
|
+
children = wysihtml5.dom.unwrap(content.firstChild);
|
14570
|
+
|
14571
|
+
// Add line break before if needed
|
14572
|
+
if (children.length > 0) {
|
14573
|
+
if (
|
14574
|
+
(fragment.lastChild && (fragment.lastChild.nodeType !== 1 || !isLineBreaking(fragment.lastChild, composer))) ||
|
14575
|
+
(!fragment.lastChild && prevNode && (prevNode.nodeType !== 1 || isLineBreaking(prevNode, composer)))
|
14576
|
+
){
|
14577
|
+
fragment.appendChild(composer.doc.createElement('BR'));
|
14578
|
+
}
|
14579
|
+
}
|
14580
|
+
|
14581
|
+
for (var c = 0, cmax = children.length; c < cmax; c++) {
|
14582
|
+
fragment.appendChild(children[c]);
|
14583
|
+
}
|
14584
|
+
|
14585
|
+
// Add line break after if needed
|
14586
|
+
if (children.length > 0) {
|
14587
|
+
if (fragment.lastChild.nodeType !== 1 || !isLineBreaking(fragment.lastChild, composer)) {
|
14588
|
+
if (nextNode || fragment.lastChild !== content.lastChild) {
|
14589
|
+
fragment.appendChild(composer.doc.createElement('BR'));
|
14590
|
+
}
|
14591
|
+
}
|
14592
|
+
}
|
14593
|
+
|
14594
|
+
} else {
|
14595
|
+
fragment.appendChild(content.firstChild);
|
14596
|
+
}
|
14597
|
+
|
14598
|
+
first = false;
|
14599
|
+
}
|
14600
|
+
blocks = wysihtml5.lang.array(fragment.childNodes).get();
|
14601
|
+
injectFragmentToRange(fragment, r, composer);
|
14602
|
+
return blocks;
|
14603
|
+
}
|
14604
|
+
|
14605
|
+
// When block node is inserted, look surrounding nodes and remove surplous linebreak tags (as block format breaks line itself)
|
14606
|
+
function removeSurroundingLineBreaks(prevNode, nextNode, composer) {
|
14607
|
+
var prevPrev = prevNode && wysihtml5.dom.domNode(prevNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
14608
|
+
if (isBr(nextNode)) {
|
14609
|
+
nextNode.parentNode.removeChild(nextNode);
|
14610
|
+
}
|
14611
|
+
if (isBr(prevNode) && (!prevPrev || prevPrev.nodeType !== 1 || composer.win.getComputedStyle(prevPrev).display !== "block")) {
|
14612
|
+
prevNode.parentNode.removeChild(prevNode);
|
14613
|
+
}
|
14614
|
+
}
|
14615
|
+
|
14616
|
+
function applySurroundingLineBreaks(prevNode, nextNode, composer) {
|
14617
|
+
var prevPrev;
|
14618
|
+
|
14619
|
+
if (prevNode && isBookmark(prevNode)) {
|
14620
|
+
prevNode = prevNode.previousSibling;
|
14621
|
+
}
|
14622
|
+
if (nextNode && isBookmark(nextNode)) {
|
14623
|
+
nextNode = nextNode.nextSibling;
|
14624
|
+
}
|
14625
|
+
|
14626
|
+
prevPrev = prevNode && prevNode.previousSibling;
|
14627
|
+
|
14628
|
+
if (prevNode && (prevNode.nodeType !== 1 || (composer.win.getComputedStyle(prevNode).display !== "block" && !isBr(prevNode))) && prevNode.parentNode) {
|
14629
|
+
prevNode.parentNode.insertBefore(composer.doc.createElement('br'), prevNode.nextSibling);
|
14630
|
+
}
|
14631
|
+
|
14632
|
+
if (nextNode && (nextNode.nodeType !== 1 || composer.win.getComputedStyle(nextNode).display !== "block") && nextNode.parentNode) {
|
14633
|
+
nextNode.parentNode.insertBefore(composer.doc.createElement('br'), nextNode);
|
14634
|
+
}
|
14635
|
+
}
|
14636
|
+
|
14637
|
+
var isWhitespaceBefore = function (textNode, offset) {
|
14638
|
+
var str = textNode.data ? textNode.data.slice(0, offset) : "";
|
14639
|
+
return (/^\s*$/).test(str);
|
14640
|
+
}
|
14641
|
+
|
14642
|
+
var isWhitespaceAfter = function (textNode, offset) {
|
14643
|
+
var str = textNode.data ? textNode.data.slice(offset) : "";
|
14644
|
+
return (/^\s*$/).test(str);
|
14645
|
+
}
|
14646
|
+
|
14647
|
+
var trimBlankTextsAndBreaks = function(fragment) {
|
14648
|
+
if (fragment) {
|
14649
|
+
while (fragment.firstChild && fragment.firstChild.nodeType === 3 && (/^\s*$/).test(fragment.firstChild.data) && fragment.lastChild !== fragment.firstChild) {
|
14650
|
+
fragment.removeChild(fragment.firstChild);
|
14651
|
+
}
|
14652
|
+
|
14653
|
+
while (fragment.lastChild && fragment.lastChild.nodeType === 3 && (/^\s*$/).test(fragment.lastChild.data) && fragment.lastChild !== fragment.firstChild) {
|
14654
|
+
fragment.removeChild(fragment.lastChild);
|
14655
|
+
}
|
14656
|
+
|
14657
|
+
if (fragment.firstChild && fragment.firstChild.nodeType === 1 && fragment.firstChild.nodeName === "BR" && fragment.lastChild !== fragment.firstChild) {
|
14658
|
+
fragment.removeChild(fragment.firstChild);
|
14282
14659
|
}
|
14660
|
+
|
14661
|
+
if (fragment.lastChild && fragment.lastChild.nodeType === 1 && fragment.lastChild.nodeName === "BR" && fragment.lastChild !== fragment.firstChild) {
|
14662
|
+
fragment.removeChild(fragment.lastChild);
|
14663
|
+
}
|
14664
|
+
}
|
14665
|
+
}
|
14666
|
+
|
14667
|
+
// Wrap the range with a block level element
|
14668
|
+
// If element is one of unnestable block elements (ex: h2 inside h1), split nodes and insert between so nesting does not occur
|
14669
|
+
function wrapRangeWithElement(range, options, closestBlockName, composer) {
|
14670
|
+
var similarOptions = options ? correctOptionsForSimilarityCheck(options) : null,
|
14671
|
+
r = range.cloneRange(),
|
14672
|
+
rangeStartContainer = r.startContainer,
|
14673
|
+
startNode = getRangeNode(r.startContainer, r.startOffset),
|
14674
|
+
endNode = getRangeNode(r.endContainer, r.endOffset),
|
14675
|
+
prevNode = (r.startContainer === startNode && startNode.nodeType === 3 && !isWhitespaceBefore(startNode, r.startOffset)) ? startNode : wysihtml5.dom.domNode(startNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true}),
|
14676
|
+
nextNode = (
|
14677
|
+
(
|
14678
|
+
r.endContainer.nodeType === 1 &&
|
14679
|
+
r.endContainer.childNodes[r.endOffset] === endNode &&
|
14680
|
+
(
|
14681
|
+
endNode.nodeType === 1 ||
|
14682
|
+
!isWhitespaceAfter(endNode, r.endOffset) &&
|
14683
|
+
!wysihtml5.dom.domNode(endNode).is.rangyBookmark()
|
14684
|
+
)
|
14685
|
+
) || (
|
14686
|
+
r.endContainer === endNode &&
|
14687
|
+
endNode.nodeType === 3 &&
|
14688
|
+
!isWhitespaceAfter(endNode, r.endOffset)
|
14689
|
+
)
|
14690
|
+
) ? endNode : wysihtml5.dom.domNode(endNode).next({nodeTypes: [1,3], ignoreBlankTexts: true}),
|
14691
|
+
content = r.extractContents(),
|
14692
|
+
fragment = composer.doc.createDocumentFragment(),
|
14693
|
+
similarOuterBlock = similarOptions ? wysihtml5.dom.getParentElement(rangeStartContainer, similarOptions, null, composer.element) : null,
|
14694
|
+
splitAllBlocks = !closestBlockName || !options || (options.nodeName === "BLOCKQUOTE" && closestBlockName === "BLOCKQUOTE"),
|
14695
|
+
firstOuterBlock = similarOuterBlock || findOuterBlock(rangeStartContainer, composer.element, splitAllBlocks), // The outermost un-nestable block element parent of selection start
|
14696
|
+
wrapper, blocks, children,
|
14697
|
+
firstc, lastC;
|
14698
|
+
|
14699
|
+
if (wysihtml5.dom.domNode(nextNode).is.rangyBookmark()) {
|
14700
|
+
endNode = nextNode;
|
14701
|
+
nextNode = endNode.nextSibling;
|
14283
14702
|
}
|
14284
14703
|
|
14704
|
+
trimBlankTextsAndBreaks(content);
|
14705
|
+
|
14706
|
+
if (options && options.nodeName === "BLOCKQUOTE") {
|
14707
|
+
|
14708
|
+
// If blockquote is to be inserted no quessing just add it as outermost block on line or selection
|
14709
|
+
var tmpEl = applyOptionsToElement(null, options, composer);
|
14710
|
+
tmpEl.appendChild(content);
|
14711
|
+
fragment.appendChild(tmpEl);
|
14712
|
+
blocks = [tmpEl];
|
14713
|
+
|
14714
|
+
} else {
|
14715
|
+
|
14716
|
+
if (!content.firstChild) {
|
14717
|
+
// IF selection is caret (can happen if line is empty) add format around tag
|
14718
|
+
fragment.appendChild(applyOptionsToElement(null, options, composer));
|
14719
|
+
} else {
|
14720
|
+
|
14721
|
+
while(content.firstChild) {
|
14722
|
+
// Iterate over all selection content first level childNodes
|
14723
|
+
|
14724
|
+
if (content.firstChild.nodeType == 1 && content.firstChild.matches(BLOCK_ELEMENTS)) {
|
14725
|
+
|
14726
|
+
// If node is a block element
|
14727
|
+
// Escape(split) block formatting at caret
|
14728
|
+
applyOptionsToElement(content.firstChild, options, composer);
|
14729
|
+
if (content.firstChild.matches(UNNESTABLE_BLOCK_ELEMENTS)) {
|
14730
|
+
unwrapBlocksFromContent(content.firstChild);
|
14731
|
+
}
|
14732
|
+
fragment.appendChild(content.firstChild);
|
14733
|
+
|
14734
|
+
} else {
|
14735
|
+
|
14736
|
+
// Wrap subsequent non-block nodes inside new block element
|
14737
|
+
wrapper = applyOptionsToElement(null, getOptionsWithNodename(options, closestBlockName, composer), composer);
|
14738
|
+
while(content.firstChild && (content.firstChild.nodeType !== 1 || !content.firstChild.matches(BLOCK_ELEMENTS))) {
|
14739
|
+
if (content.firstChild.nodeType == 1 && wrapper.matches(UNNESTABLE_BLOCK_ELEMENTS)) {
|
14740
|
+
unwrapBlocksFromContent(content.firstChild);
|
14741
|
+
}
|
14742
|
+
wrapper.appendChild(content.firstChild);
|
14743
|
+
}
|
14744
|
+
fragment.appendChild(wrapper);
|
14745
|
+
}
|
14746
|
+
}
|
14747
|
+
}
|
14748
|
+
|
14749
|
+
blocks = wysihtml5.lang.array(fragment.childNodes).get();
|
14750
|
+
}
|
14751
|
+
injectFragmentToRange(fragment, r, composer, firstOuterBlock);
|
14752
|
+
removeSurroundingLineBreaks(prevNode, nextNode, composer);
|
14753
|
+
|
14754
|
+
// Fix webkit madness by inserting linebreak rangy after cursor marker to blank last block
|
14755
|
+
// (if it contains rangy bookmark, so selection can be restored later correctly)
|
14756
|
+
if (blocks.length > 0 &&
|
14757
|
+
(
|
14758
|
+
typeof blocks[blocks.length - 1].lastChild === "undefined" || wysihtml5.dom.domNode(blocks[blocks.length - 1].lastChild).is.rangyBookmark()
|
14759
|
+
)
|
14760
|
+
) {
|
14761
|
+
blocks[blocks.length - 1].appendChild(composer.doc.createElement('br'));
|
14762
|
+
}
|
14285
14763
|
return blocks;
|
14286
14764
|
}
|
14287
14765
|
|
@@ -14293,101 +14771,181 @@ wysihtml5.Commands = Base.extend(
|
|
14293
14771
|
|
14294
14772
|
return (parentNode) ? parentNode.nodeName : null;
|
14295
14773
|
}
|
14774
|
+
|
14775
|
+
// Expands caret to cover the closest block that:
|
14776
|
+
// * cannot contain other block level elements (h1-6,p, etc)
|
14777
|
+
// * Has the same nodeName that is to be inserted
|
14778
|
+
// * has insertingNodeName
|
14779
|
+
// * is DIV if insertingNodeName is not present
|
14780
|
+
//
|
14781
|
+
// If nothing found selects the current line
|
14782
|
+
function expandCaretToBlock(composer, insertingNodeName) {
|
14783
|
+
var parent = wysihtml5.dom.getParentElement(composer.selection.getOwnRanges()[0].startContainer, {
|
14784
|
+
query: UNNESTABLE_BLOCK_ELEMENTS + ', ' + (insertingNodeName ? insertingNodeName.toLowerCase() : 'div'),
|
14785
|
+
}, null, composer.element),
|
14786
|
+
range;
|
14787
|
+
|
14788
|
+
if (parent) {
|
14789
|
+
range = composer.selection.createRange();
|
14790
|
+
range.selectNode(parent);
|
14791
|
+
composer.selection.setSelection(range);
|
14792
|
+
} else if (!composer.isEmpty()) {
|
14793
|
+
composer.selection.selectLine();
|
14794
|
+
}
|
14795
|
+
}
|
14796
|
+
|
14797
|
+
// Set selection to begin inside first created block element (beginning of it) and end inside (and after content) of last block element
|
14798
|
+
// TODO: Checking nodetype might be unnescescary as nodes inserted by formatBlock are nodetype 1 anyway
|
14799
|
+
function selectElements(newBlockElements, composer) {
|
14800
|
+
var range = composer.selection.createRange(),
|
14801
|
+
lastEl = newBlockElements[newBlockElements.length - 1],
|
14802
|
+
lastOffset = (lastEl.nodeType === 1 && lastEl.childNodes) ? lastEl.childNodes.length | 0 : lastEl.length || 0;
|
14803
|
+
|
14804
|
+
range.setStart(newBlockElements[0], 0);
|
14805
|
+
range.setEnd(lastEl, lastOffset);
|
14806
|
+
range.select();
|
14807
|
+
}
|
14808
|
+
|
14809
|
+
// Get all ranges from selection (takes out uneditables and out of editor parts) and apply format to each
|
14810
|
+
// Return created/modified block level elements
|
14811
|
+
// Method can be either "apply" or "remove"
|
14812
|
+
function formatSelection(method, composer, options) {
|
14813
|
+
var ranges = composer.selection.getOwnRanges(),
|
14814
|
+
newBlockElements = [],
|
14815
|
+
closestBlockName;
|
14816
|
+
|
14817
|
+
// Some places do not allow block level elements inbetween (inside ul and outside li, inside table and outside of td/th)
|
14818
|
+
ranges = fixNotPermittedInsertionPoints(ranges);
|
14819
|
+
|
14820
|
+
for (var i = ranges.length; i--;) {
|
14821
|
+
fixRangeCoverage(ranges[i], composer);
|
14822
|
+
closestBlockName = getParentBlockNodeName(ranges[i].startContainer, composer);
|
14823
|
+
if (method === "remove") {
|
14824
|
+
newBlockElements = newBlockElements.concat(clearRangeBlockFromating(ranges[i], closestBlockName, composer));
|
14825
|
+
} else {
|
14826
|
+
newBlockElements = newBlockElements.concat(wrapRangeWithElement(ranges[i], options, closestBlockName, composer));
|
14827
|
+
}
|
14828
|
+
}
|
14829
|
+
return newBlockElements;
|
14830
|
+
}
|
14831
|
+
|
14832
|
+
// If properties is passed as a string, look for tag with that tagName/query
|
14833
|
+
function parseOptions(options) {
|
14834
|
+
if (typeof options === "string") {
|
14835
|
+
options = {
|
14836
|
+
nodeName: options.toUpperCase()
|
14837
|
+
};
|
14838
|
+
}
|
14839
|
+
return options;
|
14840
|
+
}
|
14841
|
+
|
14842
|
+
function caretIsOnEmptyLine(composer) {
|
14843
|
+
var caretInfo;
|
14844
|
+
if (composer.selection.isCollapsed()) {
|
14845
|
+
caretInfo = composer.selection.getNodesNearCaret();
|
14846
|
+
if (caretInfo && caretInfo.caretNode) {
|
14847
|
+
if (
|
14848
|
+
// caret is allready breaknode
|
14849
|
+
wysihtml5.dom.domNode(caretInfo.caretNode).is.lineBreak() ||
|
14850
|
+
// caret is textnode
|
14851
|
+
(caretInfo.caretNode.nodeType === 3 && caretInfo.textOffset === 0 && (!caretInfo.prevNode || wysihtml5.dom.domNode(caretInfo.prevNode).is.lineBreak())) ||
|
14852
|
+
// Caret is temprorary rangy selection marker
|
14853
|
+
(caretInfo.caretNode.nodeType === 1 && caretInfo.caretNode.classList.contains('rangySelectionBoundary') &&
|
14854
|
+
(!caretInfo.prevNode || wysihtml5.dom.domNode(caretInfo.prevNode).is.lineBreak() || wysihtml5.dom.domNode(caretInfo.prevNode).is.block()) &&
|
14855
|
+
(!caretInfo.nextNode || wysihtml5.dom.domNode(caretInfo.nextNode).is.lineBreak() || wysihtml5.dom.domNode(caretInfo.nextNode).is.block())
|
14856
|
+
)
|
14857
|
+
) {
|
14858
|
+
return true;
|
14859
|
+
}
|
14860
|
+
}
|
14861
|
+
}
|
14862
|
+
return false;
|
14863
|
+
}
|
14296
14864
|
|
14297
14865
|
wysihtml5.commands.formatBlock = {
|
14298
14866
|
exec: function(composer, command, options) {
|
14867
|
+
options = parseOptions(options);
|
14299
14868
|
var newBlockElements = [],
|
14300
|
-
|
14301
|
-
|
14302
|
-
// If properties is passed as a string, look for tag with that tagName/query
|
14303
|
-
if (typeof options === "string") {
|
14304
|
-
options = {
|
14305
|
-
nodeName: options.toUpperCase()
|
14306
|
-
};
|
14307
|
-
}
|
14869
|
+
ranges, range, bookmark, state, closestBlockName;
|
14308
14870
|
|
14309
|
-
//
|
14871
|
+
// Find if current format state is active if options.toggle is set as true
|
14872
|
+
// In toggle case active state elemets are formatted instead of working directly on selection
|
14310
14873
|
if (options && options.toggle) {
|
14311
14874
|
state = this.state(composer, command, options);
|
14312
|
-
if (state) {
|
14313
|
-
bookmark = rangy.saveSelection(composer.win);
|
14314
|
-
for (var j = 0, jmax = state.length; j < jmax; j++) {
|
14315
|
-
removeOptionsFromElement(state[j], options, composer);
|
14316
|
-
}
|
14317
|
-
}
|
14318
14875
|
}
|
14876
|
+
if (state) {
|
14877
|
+
// Remove format from state nodes if toggle set and state on and selection is collapsed
|
14878
|
+
bookmark = rangy.saveSelection(composer.win);
|
14879
|
+
for (var j = 0, jmax = state.length; j < jmax; j++) {
|
14880
|
+
removeOptionsFromElement(state[j], options, composer);
|
14881
|
+
}
|
14319
14882
|
|
14320
|
-
|
14321
|
-
|
14322
|
-
|
14883
|
+
} else {
|
14884
|
+
// If selection is caret expand it to cover nearest suitable block element or row if none found
|
14323
14885
|
if (composer.selection.isCollapsed()) {
|
14324
|
-
|
14325
|
-
|
14326
|
-
}, null, composer.element);
|
14327
|
-
if (parent) {
|
14328
|
-
bookmark = rangy.saveSelection(composer.win);
|
14329
|
-
range = composer.selection.createRange();
|
14330
|
-
range.selectNode(parent);
|
14331
|
-
composer.selection.setSelection(range);
|
14332
|
-
} else if (!composer.isEmpty()) {
|
14333
|
-
bookmark = rangy.saveSelection(composer.win);
|
14886
|
+
bookmark = rangy.saveSelection(composer.win);
|
14887
|
+
if (caretIsOnEmptyLine(composer)) {
|
14334
14888
|
composer.selection.selectLine();
|
14889
|
+
} else {
|
14890
|
+
expandCaretToBlock(composer, options && options.nodeName ? options.nodeName.toUpperCase() : undefined);
|
14335
14891
|
}
|
14336
14892
|
}
|
14337
|
-
|
14338
|
-
|
14339
|
-
|
14340
|
-
|
14341
|
-
newBlockElements =
|
14893
|
+
if (options) {
|
14894
|
+
newBlockElements = formatSelection("apply", composer, options);
|
14895
|
+
} else {
|
14896
|
+
// Options == null means block formatting should be removed from selection
|
14897
|
+
newBlockElements = formatSelection("remove", composer);
|
14342
14898
|
}
|
14343
|
-
|
14899
|
+
|
14344
14900
|
}
|
14345
14901
|
|
14346
14902
|
// Remove empty block elements that may be left behind
|
14347
|
-
|
14348
|
-
|
14349
|
-
|
14350
|
-
|
14351
|
-
|
14352
|
-
|
14903
|
+
// Also remove them from new blocks list
|
14904
|
+
newBlockElements = cleanup(composer, newBlockElements);
|
14905
|
+
|
14906
|
+
// Restore selection
|
14907
|
+
if (bookmark) {
|
14908
|
+
rangy.restoreSelection(bookmark);
|
14909
|
+
} else {
|
14910
|
+
selectElements(newBlockElements, composer);
|
14353
14911
|
}
|
14354
|
-
|
14355
|
-
|
14912
|
+
},
|
14913
|
+
|
14914
|
+
// Removes all block formatting from selection
|
14915
|
+
remove: function(composer, command, options) {
|
14916
|
+
options = parseOptions(options);
|
14917
|
+
var newBlockElements, bookmark;
|
14918
|
+
|
14919
|
+
// If selection is caret expand it to cover nearest suitable block element or row if none found
|
14920
|
+
if (composer.selection.isCollapsed()) {
|
14921
|
+
bookmark = rangy.saveSelection(composer.win);
|
14922
|
+
expandCaretToBlock(composer, options && options.nodeName ? options.nodeName.toUpperCase() : undefined);
|
14923
|
+
}
|
14924
|
+
|
14925
|
+
newBlockElements = formatSelection("remove", composer);
|
14926
|
+
newBlockElements = cleanup(composer, newBlockElements);
|
14927
|
+
|
14928
|
+
// Restore selection
|
14356
14929
|
if (bookmark) {
|
14357
|
-
wysihtml5.dom.removeInvisibleSpaces(composer.element);
|
14358
14930
|
rangy.restoreSelection(bookmark);
|
14359
14931
|
} else {
|
14360
|
-
|
14361
|
-
// Set selection to beging inside first created block element (beginning of it) and end inside (and after content) of last block element
|
14362
|
-
// TODO: Checking nodetype might be unnescescary as nodes inserted by formatBlock are nodetype 1 anyway
|
14363
|
-
range = composer.selection.createRange();
|
14364
|
-
range.setStart(newBlockElements[0], 0);
|
14365
|
-
var lastEl = newBlockElements[newBlockElements.length - 1],
|
14366
|
-
lastOffset = (lastEl.nodeType === 1 && lastEl.childNodes) ? lastEl.childNodes.length | 0 : lastEl.length || 0;
|
14367
|
-
range.setEnd(lastEl, lastOffset);
|
14368
|
-
range.select();
|
14932
|
+
selectElements(newBlockElements, composer);
|
14369
14933
|
}
|
14370
14934
|
},
|
14371
14935
|
|
14372
|
-
// If
|
14373
|
-
state: function(composer, command,
|
14374
|
-
|
14375
|
-
// If properties is passed as a string, look for tag with that tagName/query
|
14376
|
-
if (typeof properties === "string") {
|
14377
|
-
properties = {
|
14378
|
-
query: properties
|
14379
|
-
};
|
14380
|
-
}
|
14936
|
+
// If options as null is passed returns status describing all block level elements
|
14937
|
+
state: function(composer, command, options) {
|
14938
|
+
options = parseOptions(options);
|
14381
14939
|
|
14382
14940
|
var nodes = composer.selection.filterElements((function (element) { // Finds matching elements inside selection
|
14383
|
-
return wysihtml5.dom.domNode(element).test(
|
14941
|
+
return wysihtml5.dom.domNode(element).test(options || { query: BLOCK_ELEMENTS });
|
14384
14942
|
}).bind(this)),
|
14385
14943
|
parentNodes = composer.selection.getSelectedOwnNodes(),
|
14386
14944
|
parent;
|
14387
14945
|
|
14388
14946
|
// Finds matching elements that are parents of selection and adds to nodes list
|
14389
14947
|
for (var i = 0, maxi = parentNodes.length; i < maxi; i++) {
|
14390
|
-
parent = dom.getParentElement(parentNodes[i],
|
14948
|
+
parent = dom.getParentElement(parentNodes[i], options || { query: BLOCK_ELEMENTS }, null, composer.element);
|
14391
14949
|
if (parent && nodes.indexOf(parent) === -1) {
|
14392
14950
|
nodes.push(parent);
|
14393
14951
|
}
|
@@ -14559,7 +15117,7 @@ wysihtml5.Commands = Base.extend(
|
|
14559
15117
|
}
|
14560
15118
|
|
14561
15119
|
// If attrbutes and values are the same > remove
|
14562
|
-
// if attributes or values
|
15120
|
+
// if attributes or values
|
14563
15121
|
function updateElementAttributes(element, newAttributes, toggle) {
|
14564
15122
|
var attr = wysihtml5.dom.getAttributes(element),
|
14565
15123
|
fullContain = containsSameAttributes(newAttributes, attr),
|
@@ -14597,6 +15155,9 @@ wysihtml5.Commands = Base.extend(
|
|
14597
15155
|
if (options.toggle !== false && element.classList.contains(options.className)) {
|
14598
15156
|
element.classList.remove(options.className);
|
14599
15157
|
} else {
|
15158
|
+
if (options.classRegExp) {
|
15159
|
+
element.className = element.className.replace(options.classRegExp, '');
|
15160
|
+
}
|
14600
15161
|
element.classList.add(options.className);
|
14601
15162
|
}
|
14602
15163
|
if (hasNoClass(element)) {
|
@@ -14630,7 +15191,7 @@ wysihtml5.Commands = Base.extend(
|
|
14630
15191
|
// Handle similar semantically same elements (queryAliasMap)
|
14631
15192
|
nodeNameQuery = options.nodeName ? queryAliasMap[options.nodeName.toLowerCase()] || options.nodeName.toLowerCase() : null;
|
14632
15193
|
nodeQueryMatch = nodeNameQuery ? wysihtml5.dom.domNode(element).test({ query: nodeNameQuery }) : false;
|
14633
|
-
|
15194
|
+
|
14634
15195
|
// Unwrap element if no attributes present and node name given
|
14635
15196
|
// or no attributes and if no nodename set but node is the default
|
14636
15197
|
if (!options.nodeName || options.nodeName === defaultTag || nodeQueryMatch) {
|
@@ -14703,7 +15264,7 @@ wysihtml5.Commands = Base.extend(
|
|
14703
15264
|
selection = rangy.getSelection(composer.win);
|
14704
15265
|
|
14705
15266
|
rangy.getSelection(composer.win).removeAllRanges();
|
14706
|
-
|
15267
|
+
|
14707
15268
|
// IE looses focus of contenteditable on removeallranges and can not set new selection unless contenteditable is focused again
|
14708
15269
|
try {
|
14709
15270
|
rangy.getSelection(composer.win).addRange(range);
|
@@ -14725,7 +15286,7 @@ wysihtml5.Commands = Base.extend(
|
|
14725
15286
|
range.setEnd(lastText, lastText.length);
|
14726
15287
|
selectRange(composer, range);
|
14727
15288
|
}
|
14728
|
-
|
15289
|
+
|
14729
15290
|
}
|
14730
15291
|
|
14731
15292
|
function selectTextNode(composer, node, start, end) {
|
@@ -14777,7 +15338,7 @@ wysihtml5.Commands = Base.extend(
|
|
14777
15338
|
}
|
14778
15339
|
|
14779
15340
|
}
|
14780
|
-
|
15341
|
+
|
14781
15342
|
return {
|
14782
15343
|
nodes: nodes,
|
14783
15344
|
partial: partial
|
@@ -14801,7 +15362,7 @@ wysihtml5.Commands = Base.extend(
|
|
14801
15362
|
}
|
14802
15363
|
|
14803
15364
|
// Returns a range and textnode containing object from caret position covering a whole word
|
14804
|
-
// wordOffsety describes the original position of caret in the new textNode
|
15365
|
+
// wordOffsety describes the original position of caret in the new textNode
|
14805
15366
|
// Caret has to be inside a textNode.
|
14806
15367
|
function getRangeForWord(selection) {
|
14807
15368
|
var anchor, offset, doc, range, offsetStart, offsetEnd, beforeChar, afterChar,
|
@@ -14849,7 +15410,7 @@ wysihtml5.Commands = Base.extend(
|
|
14849
15410
|
|
14850
15411
|
function mergeConsequentSimilarElements(elements) {
|
14851
15412
|
for (var i = elements.length; i--;) {
|
14852
|
-
|
15413
|
+
|
14853
15414
|
if (elements[i] && elements[i].parentNode) { // Test if node is not allready removed in cleanup
|
14854
15415
|
|
14855
15416
|
if (elements[i].nextSibling && isSameNode(elements[i], elements[i].nextSibling)) {
|
@@ -14933,7 +15494,7 @@ wysihtml5.Commands = Base.extend(
|
|
14933
15494
|
if (options.toggle !== false) {
|
14934
15495
|
if (caretIsInsideWord(selection)) {
|
14935
15496
|
|
14936
|
-
// Unformat whole word
|
15497
|
+
// Unformat whole word
|
14937
15498
|
wordObj = getRangeForWord(selection);
|
14938
15499
|
textNode = wordObj.textNode;
|
14939
15500
|
unformatTextNode(wordObj.textNode, composer, options);
|
@@ -14975,13 +15536,13 @@ wysihtml5.Commands = Base.extend(
|
|
14975
15536
|
}
|
14976
15537
|
|
14977
15538
|
} else {
|
14978
|
-
|
15539
|
+
|
14979
15540
|
// Selection is partially in format
|
14980
15541
|
// change it to new if format if textnode allreafy in similar state
|
14981
15542
|
// else just apply
|
14982
|
-
|
15543
|
+
|
14983
15544
|
for (i = textNodes.length; i--;) {
|
14984
|
-
|
15545
|
+
|
14985
15546
|
if (findSimilarTextNodeWrapper(textNodes[i], options, composer.element)) {
|
14986
15547
|
unformatTextNode(textNodes[i], composer, options);
|
14987
15548
|
}
|
@@ -15002,7 +15563,7 @@ wysihtml5.Commands = Base.extend(
|
|
15002
15563
|
var textNode, textOffset, newNode, i,
|
15003
15564
|
selection = composer.selection.getSelection();
|
15004
15565
|
|
15005
|
-
if (!textNodes.length) {
|
15566
|
+
if (!textNodes.length) {
|
15006
15567
|
textNode = selection.anchorNode;
|
15007
15568
|
textOffset = selection.anchorOffset;
|
15008
15569
|
|
@@ -15023,7 +15584,7 @@ wysihtml5.Commands = Base.extend(
|
|
15023
15584
|
function applyFormat(composer, textNodes, options) {
|
15024
15585
|
var wordObj, i,
|
15025
15586
|
selection = composer.selection.getSelection();
|
15026
|
-
|
15587
|
+
|
15027
15588
|
if (!textNodes.length) {
|
15028
15589
|
// Handle collapsed selection caret and return
|
15029
15590
|
if (caretIsInsideWord(selection)) {
|
@@ -15038,7 +15599,7 @@ wysihtml5.Commands = Base.extend(
|
|
15038
15599
|
formatTextRange(r, composer, options);
|
15039
15600
|
}
|
15040
15601
|
}
|
15041
|
-
|
15602
|
+
|
15042
15603
|
} else {
|
15043
15604
|
// Handle textnodes in selection and apply format
|
15044
15605
|
for (i = textNodes.length; i--;) {
|
@@ -15047,7 +15608,7 @@ wysihtml5.Commands = Base.extend(
|
|
15047
15608
|
cleanupAndSetSelection(composer, textNodes, options);
|
15048
15609
|
}
|
15049
15610
|
}
|
15050
|
-
|
15611
|
+
|
15051
15612
|
// If properties is passed as a string, correct options with that nodeName
|
15052
15613
|
function fixOptions(options) {
|
15053
15614
|
options = (typeof options === "string") ? { nodeName: options } : options;
|
@@ -15090,7 +15651,7 @@ wysihtml5.Commands = Base.extend(
|
|
15090
15651
|
// Text allready has the format applied
|
15091
15652
|
removeFormat(composer, textNodes, state, options);
|
15092
15653
|
}
|
15093
|
-
|
15654
|
+
|
15094
15655
|
composer.element.normalize();
|
15095
15656
|
},
|
15096
15657
|
|
@@ -15347,7 +15908,9 @@ wysihtml5.Commands = Base.extend(
|
|
15347
15908
|
for (var i = innerLists.length; i--;) {
|
15348
15909
|
wysihtml5.dom.resolveList(innerLists[i], composer.config.useLineBreaks);
|
15349
15910
|
}
|
15350
|
-
|
15911
|
+
if (innerLists.length === 0) {
|
15912
|
+
wysihtml5.dom.resolveList(el, composer.config.useLineBreaks);
|
15913
|
+
}
|
15351
15914
|
}
|
15352
15915
|
});
|
15353
15916
|
};
|
@@ -15383,20 +15946,19 @@ wysihtml5.Commands = Base.extend(
|
|
15383
15946
|
};
|
15384
15947
|
|
15385
15948
|
var createListFallback = function(nodeName, composer) {
|
15386
|
-
var sel;
|
15387
|
-
|
15388
|
-
if (!composer.selection.isCollapsed()) {
|
15389
|
-
sel = rangy.saveSelection(composer.win);
|
15390
|
-
}
|
15949
|
+
var sel = rangy.saveSelection(composer.win);
|
15391
15950
|
|
15392
15951
|
// Fallback for Create list
|
15393
15952
|
var tempClassName = "_wysihtml5-temp-" + new Date().getTime(),
|
15394
|
-
tempElement = composer.selection.deblockAndSurround({
|
15395
|
-
"nodeName": "div",
|
15396
|
-
"className": tempClassName
|
15397
|
-
}),
|
15398
15953
|
isEmpty, list;
|
15399
15954
|
|
15955
|
+
composer.commands.exec("formatBlock", {
|
15956
|
+
"nodeName": "div",
|
15957
|
+
"className": tempClassName
|
15958
|
+
});
|
15959
|
+
|
15960
|
+
var tempElement = composer.element.querySelector("." + tempClassName);
|
15961
|
+
|
15400
15962
|
// This space causes new lists to never break on enter
|
15401
15963
|
var INVISIBLE_SPACE_REG_EXP = /\uFEFF/g;
|
15402
15964
|
tempElement.innerHTML = tempElement.innerHTML.replace(wysihtml5.INVISIBLE_SPACE_REG_EXP, "");
|
@@ -15416,8 +15978,34 @@ wysihtml5.Commands = Base.extend(
|
|
15416
15978
|
exec: function(composer, command, nodeName) {
|
15417
15979
|
var doc = composer.doc,
|
15418
15980
|
cmd = (nodeName === "OL") ? "insertOrderedList" : "insertUnorderedList",
|
15419
|
-
|
15420
|
-
|
15981
|
+
s = composer.selection.getSelection(),
|
15982
|
+
anode = s.anchorNode.nodeType === 1 && s.anchorNode.firstChild ? s.anchorNode.childNodes[s.anchorOffset] : s.anchorNode,
|
15983
|
+
fnode = s.focusNode.nodeType === 1 && s.focusNode.firstChild ? s.focusNode.childNodes[s.focusOffset] || s.focusNode.lastChild : s.focusNode,
|
15984
|
+
selectedNode, list;
|
15985
|
+
|
15986
|
+
if (s.isBackwards()) {
|
15987
|
+
// swap variables
|
15988
|
+
anode = [fnode, fnode = anode][0];
|
15989
|
+
}
|
15990
|
+
|
15991
|
+
if (wysihtml5.dom.domNode(fnode).is.emptyTextNode(true) && fnode) {
|
15992
|
+
fnode = wysihtml5.dom.domNode(fnode).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
15993
|
+
}
|
15994
|
+
if (wysihtml5.dom.domNode(anode).is.emptyTextNode(true) && anode) {
|
15995
|
+
anode = wysihtml5.dom.domNode(anode).next({nodeTypes: [1,3], ignoreBlankTexts: true});
|
15996
|
+
}
|
15997
|
+
|
15998
|
+
if (anode && fnode) {
|
15999
|
+
if (anode === fnode) {
|
16000
|
+
selectedNode = anode;
|
16001
|
+
} else {
|
16002
|
+
selectedNode = wysihtml5.dom.domNode(anode).commonAncestor(fnode, composer.element);
|
16003
|
+
}
|
16004
|
+
} else {
|
16005
|
+
selectedNode = composer.selection.getSelectedNode();
|
16006
|
+
}
|
16007
|
+
|
16008
|
+
list = findListEl(selectedNode, nodeName, composer);
|
15421
16009
|
|
15422
16010
|
if (!list.el) {
|
15423
16011
|
if (composer.commands.support(cmd)) {
|
@@ -15442,7 +16030,7 @@ wysihtml5.Commands = Base.extend(
|
|
15442
16030
|
|
15443
16031
|
})(wysihtml5);
|
15444
16032
|
;(function(wysihtml5){
|
15445
|
-
|
16033
|
+
|
15446
16034
|
var nodeOptions = {
|
15447
16035
|
nodeName: "I",
|
15448
16036
|
toggle: true
|
@@ -15476,7 +16064,7 @@ wysihtml5.Commands = Base.extend(
|
|
15476
16064
|
return wysihtml5.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
|
15477
16065
|
}
|
15478
16066
|
};
|
15479
|
-
|
16067
|
+
|
15480
16068
|
})(wysihtml5);
|
15481
16069
|
;(function(wysihtml5) {
|
15482
16070
|
|
@@ -15533,7 +16121,7 @@ wysihtml5.Commands = Base.extend(
|
|
15533
16121
|
};
|
15534
16122
|
})(wysihtml5);
|
15535
16123
|
;(function(wysihtml5) {
|
15536
|
-
|
16124
|
+
|
15537
16125
|
var nodeOptions = {
|
15538
16126
|
styleProperty: "textAlign",
|
15539
16127
|
styleValue: "right",
|
@@ -15897,7 +16485,7 @@ wysihtml5.Commands = Base.extend(
|
|
15897
16485
|
};
|
15898
16486
|
}(wysihtml5));
|
15899
16487
|
;(function(wysihtml5){
|
15900
|
-
|
16488
|
+
|
15901
16489
|
var nodeOptions = {
|
15902
16490
|
nodeName: "SUB",
|
15903
16491
|
toggle: true
|
@@ -16247,7 +16835,7 @@ wysihtml5.views.View = Base.extend(
|
|
16247
16835
|
|
16248
16836
|
cleanUp: function(rules) {
|
16249
16837
|
var bookmark;
|
16250
|
-
if (this.selection) {
|
16838
|
+
if (this.selection && this.selection.isInThisEditable()) {
|
16251
16839
|
bookmark = rangy.saveSelection(this.win);
|
16252
16840
|
}
|
16253
16841
|
this.parent.parse(this.element, undefined, rules);
|
@@ -16419,6 +17007,8 @@ wysihtml5.views.View = Base.extend(
|
|
16419
17007
|
]).from(this.textarea.element).to(this.element);
|
16420
17008
|
}
|
16421
17009
|
|
17010
|
+
this._initAutoLinking();
|
17011
|
+
|
16422
17012
|
dom.addClass(this.element, this.config.classNames.composer);
|
16423
17013
|
//
|
16424
17014
|
// Make the editor look like the original textarea, by syncing styles
|
@@ -16451,7 +17041,6 @@ wysihtml5.views.View = Base.extend(
|
|
16451
17041
|
// Make sure that the browser avoids using inline styles whenever possible
|
16452
17042
|
this.commands.exec("styleWithCSS", false);
|
16453
17043
|
|
16454
|
-
this._initAutoLinking();
|
16455
17044
|
this._initObjectResizing();
|
16456
17045
|
this._initUndoManager();
|
16457
17046
|
this._initLineBreaking();
|
@@ -16485,10 +17074,7 @@ wysihtml5.views.View = Base.extend(
|
|
16485
17074
|
supportsAutoLinking = browser.doesAutoLinkingInContentEditable();
|
16486
17075
|
|
16487
17076
|
if (supportsDisablingOfAutoLinking) {
|
16488
|
-
// I have no idea why IE edge deletes element content here when calling the command,
|
16489
|
-
var tmpHTML = this.element.innerHTML;
|
16490
17077
|
this.commands.exec("AutoUrlDetect", false, false);
|
16491
|
-
this.element.innerHTML = tmpHTML;
|
16492
17078
|
}
|
16493
17079
|
|
16494
17080
|
if (!this.config.autoLink) {
|
@@ -16612,8 +17198,11 @@ wysihtml5.views.View = Base.extend(
|
|
16612
17198
|
function adjust(selectedNode) {
|
16613
17199
|
var parentElement = dom.getParentElement(selectedNode, { query: "p, div" }, 2);
|
16614
17200
|
if (parentElement && dom.contains(that.element, parentElement)) {
|
16615
|
-
that.selection.
|
17201
|
+
that.selection.executeAndRestoreRangy(function() {
|
16616
17202
|
if (that.config.useLineBreaks) {
|
17203
|
+
if (!parentElement.firstChild || (parentElement.firstChild === parentElement.lastChild && parentElement.firstChild.nodeType === 1 && parentElement.firstChild.classList.contains('rangySelectionBoundary'))) {
|
17204
|
+
parentElement.appendChild(that.doc.createElement('br'));
|
17205
|
+
}
|
16617
17206
|
dom.replaceWithChildNodes(parentElement);
|
16618
17207
|
} else if (parentElement.nodeName !== "P") {
|
16619
17208
|
dom.renameElement(parentElement, "p");
|
@@ -16622,59 +17211,63 @@ wysihtml5.views.View = Base.extend(
|
|
16622
17211
|
}
|
16623
17212
|
}
|
16624
17213
|
|
17214
|
+
// Ensures when editor is empty and not line breaks mode, the inital state has a paragraph in it on focus with caret inside paragraph
|
16625
17215
|
if (!this.config.useLineBreaks) {
|
16626
|
-
dom.observe(this.element, ["focus"
|
17216
|
+
dom.observe(this.element, ["focus"], function() {
|
16627
17217
|
if (that.isEmpty()) {
|
16628
|
-
|
16629
|
-
|
16630
|
-
|
16631
|
-
|
16632
|
-
|
16633
|
-
|
16634
|
-
|
16635
|
-
|
16636
|
-
|
17218
|
+
setTimeout(function() {
|
17219
|
+
var paragraph = that.doc.createElement("P");
|
17220
|
+
that.element.innerHTML = "";
|
17221
|
+
that.element.appendChild(paragraph);
|
17222
|
+
if (!browser.displaysCaretInEmptyContentEditableCorrectly()) {
|
17223
|
+
paragraph.innerHTML = "<br>";
|
17224
|
+
that.selection.setBefore(paragraph.firstChild);
|
17225
|
+
} else {
|
17226
|
+
that.selection.selectNode(paragraph, true);
|
17227
|
+
}
|
17228
|
+
}, 0);
|
16637
17229
|
}
|
16638
17230
|
});
|
16639
17231
|
}
|
17232
|
+
|
16640
17233
|
dom.observe(this.element, "keydown", function(event) {
|
16641
17234
|
var keyCode = event.keyCode;
|
16642
17235
|
|
16643
|
-
if (event.ctrlKey || event.defaultPrevented) {
|
17236
|
+
if (event.shiftKey || event.ctrlKey || event.defaultPrevented) {
|
16644
17237
|
return;
|
16645
17238
|
}
|
16646
17239
|
|
16647
17240
|
if (keyCode !== wysihtml5.ENTER_KEY && keyCode !== wysihtml5.BACKSPACE_KEY) {
|
16648
17241
|
return;
|
16649
17242
|
}
|
16650
|
-
|
16651
|
-
|
16652
|
-
|
16653
|
-
|
16654
|
-
|
16655
|
-
|
16656
|
-
setTimeout(function() {
|
16657
|
-
// Unwrap paragraph after leaving a list or a H1-6
|
16658
|
-
var selectedNode = that.selection.getSelectedNode(),
|
16659
|
-
list;
|
16660
|
-
|
16661
|
-
if (blockElement.nodeName === "LI") {
|
16662
|
-
if (!selectedNode) {
|
16663
|
-
return;
|
16664
|
-
}
|
16665
|
-
|
16666
|
-
list = dom.getParentElement(selectedNode, { query: LIST_TAGS }, 2);
|
17243
|
+
var blockElement = dom.getParentElement(that.selection.getSelectedNode(), { query: USE_NATIVE_LINE_BREAK_INSIDE_TAGS }, 4);
|
17244
|
+
if (blockElement) {
|
17245
|
+
setTimeout(function() {
|
17246
|
+
// Unwrap paragraph after leaving a list or a H1-6
|
17247
|
+
var selectedNode = that.selection.getSelectedNode(),
|
17248
|
+
list;
|
16667
17249
|
|
16668
|
-
|
16669
|
-
|
16670
|
-
|
17250
|
+
if (blockElement.nodeName === "LI") {
|
17251
|
+
if (!selectedNode) {
|
17252
|
+
return;
|
16671
17253
|
}
|
16672
17254
|
|
16673
|
-
|
17255
|
+
list = dom.getParentElement(selectedNode, { query: LIST_TAGS }, 2);
|
17256
|
+
|
17257
|
+
if (!list) {
|
16674
17258
|
adjust(selectedNode);
|
16675
17259
|
}
|
16676
|
-
}
|
16677
|
-
|
17260
|
+
}
|
17261
|
+
|
17262
|
+
if (keyCode === wysihtml5.ENTER_KEY && blockElement.nodeName.match(/^H[1-6]$/)) {
|
17263
|
+
adjust(selectedNode);
|
17264
|
+
}
|
17265
|
+
}, 0);
|
17266
|
+
return;
|
17267
|
+
}
|
17268
|
+
if (that.config.useLineBreaks && keyCode === wysihtml5.ENTER_KEY && !wysihtml5.browser.insertsLineBreaksOnReturn()) {
|
17269
|
+
event.preventDefault();
|
17270
|
+
that.commands.exec("insertLineBreak");
|
16678
17271
|
}
|
16679
17272
|
});
|
16680
17273
|
}
|
@@ -16891,6 +17484,7 @@ wysihtml5.views.View = Base.extend(
|
|
16891
17484
|
*/
|
16892
17485
|
(function(wysihtml5) {
|
16893
17486
|
var dom = wysihtml5.dom,
|
17487
|
+
domNode = dom.domNode,
|
16894
17488
|
browser = wysihtml5.browser,
|
16895
17489
|
/**
|
16896
17490
|
* Map keyCodes to query commands
|
@@ -16900,95 +17494,253 @@ wysihtml5.views.View = Base.extend(
|
|
16900
17494
|
"73": "italic", // I
|
16901
17495
|
"85": "underline" // U
|
16902
17496
|
};
|
17497
|
+
|
17498
|
+
var actions = {
|
16903
17499
|
|
16904
|
-
|
16905
|
-
|
16906
|
-
|
16907
|
-
|
16908
|
-
|
16909
|
-
|
16910
|
-
|
17500
|
+
// Adds multiple eventlisteners to target, bound to one callback
|
17501
|
+
// TODO: If needed elsewhere make it part of wysihtml5.dom or sth
|
17502
|
+
addListeners: function (target, events, callback) {
|
17503
|
+
for(var i = 0, max = events.length; i < max; i++) {
|
17504
|
+
target.addEventListener(events[i], callback, false);
|
17505
|
+
}
|
17506
|
+
},
|
16911
17507
|
|
16912
|
-
|
16913
|
-
|
16914
|
-
|
16915
|
-
|
16916
|
-
|
16917
|
-
|
16918
|
-
|
17508
|
+
// Removes multiple eventlisteners from target, bound to one callback
|
17509
|
+
// TODO: If needed elsewhere make it part of wysihtml5.dom or sth
|
17510
|
+
removeListeners: function (target, events, callback) {
|
17511
|
+
for(var i = 0, max = events.length; i < max; i++) {
|
17512
|
+
target.removeEventListener(events[i], callback, false);
|
17513
|
+
}
|
17514
|
+
},
|
16919
17515
|
|
16920
|
-
|
16921
|
-
|
16922
|
-
|
16923
|
-
|
16924
|
-
|
16925
|
-
|
16926
|
-
|
16927
|
-
|
16928
|
-
|
16929
|
-
|
17516
|
+
// Override for giving user ability to delete last line break in table cell
|
17517
|
+
fixLastBrDeletionInTable: function(composer, force) {
|
17518
|
+
if (composer.selection.caretIsLastInSelection()) {
|
17519
|
+
var sel = composer.selection.getSelection(),
|
17520
|
+
aNode = sel.anchorNode;
|
17521
|
+
if (aNode && aNode.nodeType === 1 && (wysihtml5.dom.getParentElement(aNode, {query: 'td, th'}, false, composer.element) || force)) {
|
17522
|
+
var nextNode = aNode.childNodes[sel.anchorOffset];
|
17523
|
+
if (nextNode && nextNode.nodeType === 1 & nextNode.nodeName === "BR") {
|
17524
|
+
nextNode.parentNode.removeChild(nextNode);
|
17525
|
+
return true;
|
17526
|
+
}
|
16930
17527
|
}
|
16931
17528
|
}
|
16932
|
-
|
16933
|
-
|
16934
|
-
};
|
17529
|
+
return false;
|
17530
|
+
},
|
16935
17531
|
|
16936
|
-
|
16937
|
-
|
16938
|
-
|
16939
|
-
|
16940
|
-
|
17532
|
+
// If found an uneditable before caret then notify it before deletion
|
17533
|
+
handleUneditableDeletion: function(composer) {
|
17534
|
+
var before = composer.selection.getBeforeSelection(true);
|
17535
|
+
if (before && (before.type === "element" || before.type === "leafnode") && before.node.nodeType === 1 && before.node.classList.contains(composer.config.classNames.uneditableContainer)) {
|
17536
|
+
if (actions.fixLastBrDeletionInTable(composer, true)) {
|
17537
|
+
return true;
|
17538
|
+
}
|
17539
|
+
try {
|
17540
|
+
var ev = new CustomEvent("wysihtml5:uneditable:delete", {bubbles: true, cancelable: false});
|
17541
|
+
before.node.dispatchEvent(ev);
|
17542
|
+
} catch (err) {}
|
17543
|
+
before.node.parentNode.removeChild(before.node);
|
16941
17544
|
return true;
|
16942
17545
|
}
|
16943
|
-
|
16944
|
-
|
16945
|
-
before.node.dispatchEvent(ev);
|
16946
|
-
} catch (err) {}
|
16947
|
-
before.node.parentNode.removeChild(before.node);
|
16948
|
-
return true;
|
16949
|
-
}
|
16950
|
-
return false;
|
16951
|
-
};
|
17546
|
+
return false;
|
17547
|
+
},
|
16952
17548
|
|
16953
|
-
|
16954
|
-
|
16955
|
-
|
16956
|
-
|
16957
|
-
|
17549
|
+
// Deletion with caret in the beginning of headings and other block elvel elements needs special attention
|
17550
|
+
// Not allways does it concate text to previous block node correctly (browsers do unexpected miracles here especially webkit)
|
17551
|
+
fixDeleteInTheBeginningOfBlock: function(composer) {
|
17552
|
+
var selection = composer.selection,
|
17553
|
+
prevNode = selection.getPreviousNode();
|
16958
17554
|
|
16959
|
-
|
16960
|
-
prevNode &&
|
16961
|
-
|
16962
|
-
|
16963
|
-
|
16964
|
-
|
16965
|
-
|
16966
|
-
|
17555
|
+
if (selection.caretIsFirstInSelection(wysihtml5.browser.usesControlRanges()) && prevNode) {
|
17556
|
+
if (prevNode.nodeType === 1 &&
|
17557
|
+
wysihtml5.dom.domNode(prevNode).is.block() &&
|
17558
|
+
!domNode(prevNode).test({
|
17559
|
+
query: "ol, ul, table, tr, dl"
|
17560
|
+
})
|
17561
|
+
) {
|
17562
|
+
if ((/^\s*$/).test(prevNode.textContent || prevNode.innerText)) {
|
17563
|
+
// If heading is empty remove the heading node
|
17564
|
+
prevNode.parentNode.removeChild(prevNode);
|
17565
|
+
return true;
|
17566
|
+
} else {
|
17567
|
+
if (prevNode.lastChild) {
|
17568
|
+
var selNode = prevNode.lastChild,
|
17569
|
+
selectedNode = selection.getSelectedNode(),
|
17570
|
+
commonAncestorNode = domNode(prevNode).commonAncestor(selectedNode, composer.element),
|
17571
|
+
curNode = wysihtml5.dom.getParentElement(selectedNode, {
|
17572
|
+
query: "h1, h2, h3, h4, h5, h6, p, pre, div, blockquote"
|
17573
|
+
}, false, commonAncestorNode || composer.element);
|
17574
|
+
|
17575
|
+
if (curNode) {
|
17576
|
+
domNode(curNode).transferContentTo(prevNode, true);
|
17577
|
+
selection.setAfter(selNode);
|
17578
|
+
return true;
|
17579
|
+
} else if (wysihtml5.browser.usesControlRanges()) {
|
17580
|
+
selectedNode = selection.getCaretNode();
|
17581
|
+
domNode(selectedNode).transferContentTo(prevNode, true);
|
17582
|
+
selection.setAfter(selNode);
|
17583
|
+
return true;
|
17584
|
+
}
|
17585
|
+
}
|
17586
|
+
}
|
17587
|
+
}
|
17588
|
+
}
|
17589
|
+
return false;
|
17590
|
+
},
|
17591
|
+
|
17592
|
+
/* In IE when deleting with caret at the begining of LI, list gets broken into half instead of merging the LI with previous */
|
17593
|
+
/* This does not match other browsers an is less intuitive from UI standpoint, thus has to be fixed */
|
17594
|
+
fixDeleteInTheBeginningOfLi: function(composer) {
|
17595
|
+
if (wysihtml5.browser.hasLiDeletingProblem()) {
|
17596
|
+
var selection = composer.selection.getSelection(),
|
17597
|
+
aNode = selection.anchorNode,
|
17598
|
+
listNode, prevNode, firstNode,
|
17599
|
+
isInBeginnig = composer.selection.caretIsFirstInSelection();
|
17600
|
+
|
17601
|
+
// Fix caret at the beginnig of first textNode in LI
|
17602
|
+
if (aNode.nodeType === 3 && selection.anchorOffset === 0 && aNode === aNode.parentNode.firstChild) {
|
17603
|
+
aNode = aNode.parentNode;
|
17604
|
+
isInBeginnig = true;
|
17605
|
+
}
|
17606
|
+
|
17607
|
+
if (isInBeginnig && aNode && aNode.nodeType === 1 && aNode.nodeName === "LI") {
|
17608
|
+
prevNode = domNode(aNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
17609
|
+
if (!prevNode && aNode.parentNode && (aNode.parentNode.nodeName === "UL" || aNode.parentNode.nodeName === "OL")) {
|
17610
|
+
prevNode = domNode(aNode.parentNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true});
|
17611
|
+
}
|
17612
|
+
if (prevNode) {
|
17613
|
+
firstNode = aNode.firstChild;
|
17614
|
+
domNode(aNode).transferContentTo(prevNode, true);
|
17615
|
+
if (firstNode) {
|
17616
|
+
composer.selection.setBefore(firstNode);
|
17617
|
+
} else if (prevNode) {
|
17618
|
+
if (prevNode.nodeType === 1) {
|
17619
|
+
if (prevNode.lastChild) {
|
17620
|
+
composer.selection.setAfter(prevNode.lastChild);
|
17621
|
+
} else {
|
17622
|
+
composer.selection.selectNode(prevNode);
|
17623
|
+
}
|
17624
|
+
} else {
|
17625
|
+
composer.selection.setAfter(prevNode);
|
17626
|
+
}
|
17627
|
+
}
|
17628
|
+
return true;
|
17629
|
+
}
|
17630
|
+
}
|
17631
|
+
}
|
17632
|
+
return false;
|
17633
|
+
},
|
17634
|
+
|
17635
|
+
fixDeleteInTheBeginningOfControlSelection: function(composer) {
|
17636
|
+
var selection = composer.selection,
|
17637
|
+
prevNode = selection.getPreviousNode(),
|
17638
|
+
selectedNode = selection.getSelectedNode(),
|
17639
|
+
afterCaretNode;
|
17640
|
+
|
17641
|
+
if (selection.caretIsFirstInSelection()) {
|
17642
|
+
if (selectedNode.nodeType === 3) {
|
17643
|
+
selectedNode = selectedNode.parentNode;
|
17644
|
+
}
|
17645
|
+
afterCaretNode = selectedNode.firstChild;
|
17646
|
+
domNode(selectedNode).transferContentTo(prevNode, true);
|
17647
|
+
if (afterCaretNode) {
|
17648
|
+
composer.selection.setBefore(afterCaretNode);
|
17649
|
+
}
|
16967
17650
|
return true;
|
16968
|
-
}
|
16969
|
-
|
16970
|
-
|
16971
|
-
|
16972
|
-
|
16973
|
-
|
16974
|
-
|
16975
|
-
|
16976
|
-
|
16977
|
-
|
16978
|
-
|
16979
|
-
|
17651
|
+
}
|
17652
|
+
return false;
|
17653
|
+
},
|
17654
|
+
|
17655
|
+
// Table management
|
17656
|
+
// If present enableObjectResizing and enableInlineTableEditing command should be called with false to prevent native table handlers
|
17657
|
+
initTableHandling: function() {
|
17658
|
+
var hideHandlers = function() {
|
17659
|
+
window.removeEventListener('load', hideHandlers);
|
17660
|
+
this.doc.execCommand("enableObjectResizing", false, "false");
|
17661
|
+
this.doc.execCommand("enableInlineTableEditing", false, "false");
|
17662
|
+
}.bind(this),
|
17663
|
+
iframeInitiator = (function() {
|
17664
|
+
hideHandlers.call(this);
|
17665
|
+
actions.removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
17666
|
+
}).bind(this);
|
17667
|
+
|
17668
|
+
if( this.doc.execCommand &&
|
17669
|
+
wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
|
17670
|
+
wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
|
17671
|
+
{
|
17672
|
+
if (this.sandbox.getIframe) {
|
17673
|
+
actions.addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
17674
|
+
} else {
|
17675
|
+
window.addEventListener('load', hideHandlers);
|
17676
|
+
}
|
17677
|
+
}
|
17678
|
+
this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
|
17679
|
+
},
|
17680
|
+
|
17681
|
+
// Fixes some misbehaviours of enters in linebreaks mode (natively a bit unsupported feature)
|
17682
|
+
// Returns true if some corrections is applied so events know when to prevent default
|
17683
|
+
doLineBreaksModeEnterWithCaret: function(composer) {
|
17684
|
+
var breakNodes = "p, pre, div, blockquote",
|
17685
|
+
caretInfo, parent, txtNode,
|
17686
|
+
ret = false;
|
17687
|
+
|
17688
|
+
caretInfo = composer.selection.getNodesNearCaret();
|
17689
|
+
if (caretInfo) {
|
17690
|
+
|
17691
|
+
if (caretInfo.caretNode || caretInfo.nextNode) {
|
17692
|
+
parent = dom.getParentElement(caretInfo.caretNode || caretInfo.nextNode, { query: breakNodes }, 2);
|
17693
|
+
if (parent === composer.element) {
|
17694
|
+
parent = undefined;
|
17695
|
+
}
|
17696
|
+
}
|
17697
|
+
|
17698
|
+
if (parent && caretInfo.caretNode) {
|
17699
|
+
if (domNode(caretInfo.caretNode).is.lineBreak()) {
|
17700
|
+
|
17701
|
+
if (composer.config.doubleLineBreakEscapesBlock) {
|
17702
|
+
// Double enter (enter on blank line) exits block element in useLineBreaks mode.
|
17703
|
+
ret = true;
|
17704
|
+
caretInfo.caretNode.parentNode.removeChild(caretInfo.caretNode);
|
17705
|
+
|
17706
|
+
// Ensure surplous line breaks are not added to preceding element
|
17707
|
+
if (domNode(caretInfo.nextNode).is.lineBreak()) {
|
17708
|
+
caretInfo.nextNode.parentNode.removeChild(caretInfo.nextNode);
|
17709
|
+
}
|
17710
|
+
|
17711
|
+
var brNode = composer.doc.createElement('br');
|
17712
|
+
if (domNode(caretInfo.nextNode).is.lineBreak() && caretInfo.nextNode === parent.lastChild) {
|
17713
|
+
parent.parentNode.insertBefore(brNode, parent.nextSibling);
|
17714
|
+
} else {
|
17715
|
+
composer.selection.splitElementAtCaret(parent, brNode);
|
16980
17716
|
}
|
16981
|
-
|
16982
|
-
|
16983
|
-
|
16984
|
-
|
16985
|
-
|
16986
|
-
|
17717
|
+
|
17718
|
+
// Ensure surplous blank lines are not added to preceding element
|
17719
|
+
if (caretInfo.nextNode && caretInfo.nextNode.nodeType === 3) {
|
17720
|
+
// Replaces blank lines at the beginning of textnode
|
17721
|
+
caretInfo.nextNode.data = caretInfo.nextNode.data.replace(/^ *[\r\n]+/, '');
|
17722
|
+
}
|
17723
|
+
composer.selection.setBefore(brNode);
|
16987
17724
|
}
|
17725
|
+
|
17726
|
+
} else if (caretInfo.caretNode.nodeType === 3 && wysihtml5.browser.hasCaretBlockElementIssue() && caretInfo.textOffset === caretInfo.caretNode.data.length && !caretInfo.nextNode) {
|
17727
|
+
|
17728
|
+
// This fixes annoying webkit issue when you press enter at the end of a block then seemingly nothing happens.
|
17729
|
+
// in reality one line break is generated and cursor is reported after it, but when entering something cursor jumps before the br
|
17730
|
+
ret = true;
|
17731
|
+
var br1 = composer.doc.createElement('br'),
|
17732
|
+
br2 = composer.doc.createElement('br'),
|
17733
|
+
f = composer.doc.createDocumentFragment();
|
17734
|
+
f.appendChild(br1);
|
17735
|
+
f.appendChild(br2);
|
17736
|
+
composer.selection.insertNode(f);
|
17737
|
+
composer.selection.setBefore(br2);
|
17738
|
+
|
17739
|
+
}
|
16988
17740
|
}
|
16989
17741
|
}
|
17742
|
+
return ret;
|
16990
17743
|
}
|
16991
|
-
return false;
|
16992
17744
|
};
|
16993
17745
|
|
16994
17746
|
var handleDeleteKeyPress = function(event, composer) {
|
@@ -16996,18 +17748,28 @@ wysihtml5.views.View = Base.extend(
|
|
16996
17748
|
element = composer.element;
|
16997
17749
|
|
16998
17750
|
if (selection.isCollapsed()) {
|
16999
|
-
if (
|
17751
|
+
if (actions.handleUneditableDeletion(composer)) {
|
17752
|
+
event.preventDefault();
|
17753
|
+
return;
|
17754
|
+
}
|
17755
|
+
if (actions.fixDeleteInTheBeginningOfLi(composer)) {
|
17000
17756
|
event.preventDefault();
|
17001
17757
|
return;
|
17002
17758
|
}
|
17003
|
-
if (
|
17759
|
+
if (actions.fixDeleteInTheBeginningOfBlock(composer)) {
|
17004
17760
|
event.preventDefault();
|
17005
17761
|
return;
|
17006
17762
|
}
|
17007
|
-
if (
|
17763
|
+
if (actions.fixLastBrDeletionInTable(composer)) {
|
17008
17764
|
event.preventDefault();
|
17009
17765
|
return;
|
17010
17766
|
}
|
17767
|
+
if (wysihtml5.browser.usesControlRanges()) {
|
17768
|
+
if (actions.fixDeleteInTheBeginningOfControlSelection(composer)) {
|
17769
|
+
event.preventDefault();
|
17770
|
+
return;
|
17771
|
+
}
|
17772
|
+
}
|
17011
17773
|
} else {
|
17012
17774
|
if (selection.containsUneditable()) {
|
17013
17775
|
event.preventDefault();
|
@@ -17016,6 +17778,21 @@ wysihtml5.views.View = Base.extend(
|
|
17016
17778
|
}
|
17017
17779
|
};
|
17018
17780
|
|
17781
|
+
var handleEnterKeyPress = function(event, composer) {
|
17782
|
+
if (composer.config.useLineBreaks && !event.shiftKey && !event.ctrlKey) {
|
17783
|
+
// Fixes some misbehaviours of enters in linebreaks mode (natively a bit unsupported feature)
|
17784
|
+
|
17785
|
+
var breakNodes = "p, pre, div, blockquote",
|
17786
|
+
caretInfo, parent, txtNode;
|
17787
|
+
|
17788
|
+
if (composer.selection.isCollapsed()) {
|
17789
|
+
if (actions.doLineBreaksModeEnterWithCaret(composer)) {
|
17790
|
+
event.preventDefault();
|
17791
|
+
}
|
17792
|
+
}
|
17793
|
+
}
|
17794
|
+
};
|
17795
|
+
|
17019
17796
|
var handleTabKeyDown = function(composer, element, shiftKey) {
|
17020
17797
|
if (!composer.selection.isCollapsed()) {
|
17021
17798
|
composer.selection.deleteContents();
|
@@ -17109,25 +17886,29 @@ wysihtml5.views.View = Base.extend(
|
|
17109
17886
|
this.selection.selectNode(target);
|
17110
17887
|
}
|
17111
17888
|
}
|
17112
|
-
};
|
17113
|
-
|
17114
|
-
// TODO: mouseover is not actually a foolproof and obvious place for this, must be changed as it modifies dom on random basis
|
17115
|
-
// Shows url in tooltip when hovering links or images
|
17116
|
-
var handleMouseOver = function(event) {
|
17117
|
-
var titlePrefixes = {
|
17118
|
-
IMG: "Image: ",
|
17119
|
-
A: "Link: "
|
17120
|
-
},
|
17121
|
-
target = event.target,
|
17122
|
-
nodeName = target.nodeName,
|
17123
|
-
title;
|
17124
17889
|
|
17125
|
-
|
17126
|
-
|
17890
|
+
// Saves mousedown position for IE controlSelect fix
|
17891
|
+
if (wysihtml5.browser.usesControlRanges()) {
|
17892
|
+
this.selection.lastMouseDownPos = {x: event.clientX, y: event.clientY};
|
17893
|
+
setTimeout(function() {
|
17894
|
+
delete this.selection.lastMouseDownPos;
|
17895
|
+
}.bind(this), 0);
|
17127
17896
|
}
|
17128
|
-
|
17129
|
-
|
17130
|
-
|
17897
|
+
};
|
17898
|
+
|
17899
|
+
// IE has this madness of control selects of overflowed and some other elements (weird box around element on selection and second click selects text)
|
17900
|
+
// This fix handles the second click problem by adding cursor to the right position under cursor inside when controlSelection is made
|
17901
|
+
var handleIEControlSelect = function(event) {
|
17902
|
+
var target = event.target,
|
17903
|
+
pos = this.selection.lastMouseDownPos;
|
17904
|
+
if (pos) {
|
17905
|
+
var caretPosition = document.body.createTextRange();
|
17906
|
+
setTimeout(function() {
|
17907
|
+
try {
|
17908
|
+
caretPosition.moveToPoint(pos.x, pos.y);
|
17909
|
+
caretPosition.select();
|
17910
|
+
} catch (e) {}
|
17911
|
+
}.bind(this), 0);
|
17131
17912
|
}
|
17132
17913
|
};
|
17133
17914
|
|
@@ -17197,6 +17978,10 @@ wysihtml5.views.View = Base.extend(
|
|
17197
17978
|
handleTabKeyDown(this, this.element, event.shiftKey);
|
17198
17979
|
}
|
17199
17980
|
|
17981
|
+
if (keyCode === wysihtml5.ENTER_KEY) {
|
17982
|
+
handleEnterKeyPress(event, this);
|
17983
|
+
}
|
17984
|
+
|
17200
17985
|
};
|
17201
17986
|
|
17202
17987
|
var handleIframeFocus = function(event) {
|
@@ -17212,32 +17997,9 @@ wysihtml5.views.View = Base.extend(
|
|
17212
17997
|
this.selection.getSelection().removeAllRanges();
|
17213
17998
|
}).bind(this), 0);
|
17214
17999
|
};
|
17215
|
-
|
17216
|
-
//
|
17217
|
-
|
17218
|
-
var initTableHandling = function () {
|
17219
|
-
var hideHandlers = function () {
|
17220
|
-
window.removeEventListener('load', hideHandlers);
|
17221
|
-
this.doc.execCommand("enableObjectResizing", false, "false");
|
17222
|
-
this.doc.execCommand("enableInlineTableEditing", false, "false");
|
17223
|
-
}.bind(this),
|
17224
|
-
iframeInitiator = (function() {
|
17225
|
-
hideHandlers.call(this);
|
17226
|
-
removeListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
17227
|
-
}).bind(this);
|
17228
|
-
|
17229
|
-
if( this.doc.execCommand &&
|
17230
|
-
wysihtml5.browser.supportsCommand(this.doc, "enableObjectResizing") &&
|
17231
|
-
wysihtml5.browser.supportsCommand(this.doc, "enableInlineTableEditing"))
|
17232
|
-
{
|
17233
|
-
if (this.sandbox.getIframe) {
|
17234
|
-
addListeners(this.sandbox.getIframe(), ["focus", "mouseup", "mouseover"], iframeInitiator);
|
17235
|
-
} else {
|
17236
|
-
window.addEventListener('load', hideHandlers);
|
17237
|
-
}
|
17238
|
-
}
|
17239
|
-
this.tableSelection = wysihtml5.quirks.tableCellsSelection(this.element, this.parent);
|
17240
|
-
};
|
18000
|
+
|
18001
|
+
// Testing requires actions to be accessible from out of scope
|
18002
|
+
wysihtml5.views.Composer.prototype.observeActions = actions;
|
17241
18003
|
|
17242
18004
|
wysihtml5.views.Composer.prototype.observe = function() {
|
17243
18005
|
var that = this,
|
@@ -17248,7 +18010,19 @@ wysihtml5.views.View = Base.extend(
|
|
17248
18010
|
this.focusState = this.getValue(false, false);
|
17249
18011
|
|
17250
18012
|
// --------- destroy:composer event ---------
|
17251
|
-
|
18013
|
+
|
18014
|
+
// FIXME: (did) replace the deprecated DOMNodeRemoved event by a call to the MutationObserver
|
18015
|
+
const handleRemovedNodes = function(records) {
|
18016
|
+
for (const record of records) {
|
18017
|
+
if (record.removedNodes.length > 0) {
|
18018
|
+
if (record.removedNodes[0] === container) {
|
18019
|
+
handleDomNodeRemoved.call(this)
|
18020
|
+
}
|
18021
|
+
}
|
18022
|
+
}
|
18023
|
+
}
|
18024
|
+
const observer = new MutationObserver(handleRemovedNodes.bind(this));
|
18025
|
+
observer.observe(container.parentNode, { childList: true });
|
17252
18026
|
|
17253
18027
|
// DOMNodeRemoved event is not supported in IE 8
|
17254
18028
|
// TODO: try to figure out a polyfill style fix, so it could be transferred to polyfills and removed if ie8 is not needed
|
@@ -17263,22 +18037,26 @@ wysihtml5.views.View = Base.extend(
|
|
17263
18037
|
// --------- User interactions --
|
17264
18038
|
if (this.config.handleTables) {
|
17265
18039
|
// If handleTables option is true, table handling functions are bound
|
17266
|
-
initTableHandling.call(this);
|
18040
|
+
actions.initTableHandling.call(this);
|
17267
18041
|
}
|
17268
18042
|
|
17269
|
-
addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
|
18043
|
+
actions.addListeners(focusBlurElement, ["drop", "paste", "mouseup", "focus", "keyup"], handleUserInteraction.bind(this));
|
17270
18044
|
focusBlurElement.addEventListener("focus", handleFocus.bind(this), false);
|
17271
18045
|
focusBlurElement.addEventListener("blur", handleBlur.bind(this), false);
|
17272
|
-
|
17273
|
-
addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
|
18046
|
+
|
18047
|
+
actions.addListeners(this.element, ["drop", "paste", "beforepaste"], handlePaste.bind(this), false);
|
17274
18048
|
this.element.addEventListener("copy", handleCopy.bind(this), false);
|
17275
18049
|
this.element.addEventListener("mousedown", handleMouseDown.bind(this), false);
|
17276
|
-
this.element.addEventListener("mouseover", handleMouseOver.bind(this), false);
|
17277
18050
|
this.element.addEventListener("click", handleClick.bind(this), false);
|
17278
18051
|
this.element.addEventListener("drop", handleDrop.bind(this), false);
|
17279
18052
|
this.element.addEventListener("keyup", handleKeyUp.bind(this), false);
|
17280
18053
|
this.element.addEventListener("keydown", handleKeyDown.bind(this), false);
|
17281
18054
|
|
18055
|
+
// IE controlselect madness fix
|
18056
|
+
if (wysihtml5.browser.usesControlRanges()) {
|
18057
|
+
this.element.addEventListener('mscontrolselect', handleIEControlSelect.bind(this), false);
|
18058
|
+
}
|
18059
|
+
|
17282
18060
|
this.element.addEventListener("dragenter", (function() {
|
17283
18061
|
this.parent.fire("unset_placeholder");
|
17284
18062
|
}).bind(this), false);
|
@@ -17569,6 +18347,9 @@ wysihtml5.views.View = Base.extend(
|
|
17569
18347
|
parser: wysihtml5.dom.parse,
|
17570
18348
|
// By default wysihtml5 will insert a <br> for line breaks, set this to false to use <p>
|
17571
18349
|
useLineBreaks: true,
|
18350
|
+
// Double enter (enter on blank line) exits block element in useLineBreaks mode.
|
18351
|
+
// It enables a way of escaping out of block elements and splitting block elements
|
18352
|
+
doubleLineBreakEscapesBlock: true,
|
17572
18353
|
// Array (or single string) of stylesheet urls to be loaded in the editor's iframe
|
17573
18354
|
stylesheets: [],
|
17574
18355
|
// Placeholder text to use, defaults to the placeholder attribute on the textarea element
|
@@ -17592,7 +18373,7 @@ wysihtml5.views.View = Base.extend(
|
|
17592
18373
|
uneditableContainer: "wysihtml5-uneditable-container"
|
17593
18374
|
},
|
17594
18375
|
// Browsers that support copied source handling will get a marking of the origin of the copied source (for determinig code cleanup rules on paste)
|
17595
|
-
// Also copied source is based directly on selection -
|
18376
|
+
// Also copied source is based directly on selection -
|
17596
18377
|
// (very useful for webkit based browsers where copy will otherwise contain a lot of code and styles based on whatever and not actually in selection).
|
17597
18378
|
// If falsy value is passed source override is also disabled
|
17598
18379
|
copyedFromMarking: '<meta name="copied-from" content="wysihtml5">'
|
@@ -18339,6 +19120,7 @@ wysihtml5.views.View = Base.extend(
|
|
18339
19120
|
},
|
18340
19121
|
|
18341
19122
|
_updateLinkStates: function() {
|
19123
|
+
|
18342
19124
|
var i, state, action, command, displayDialogAttributeValue,
|
18343
19125
|
commandMapping = this.commandMapping,
|
18344
19126
|
composer = this.composer,
|