type_station 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/type_station/editables/admin_bar.js.coffee +19 -48
- data/app/assets/javascripts/type_station/editables/entity_editor.js.coffee +152 -0
- data/app/assets/javascripts/type_station/editables/file_editor.js.coffee +46 -0
- data/app/assets/javascripts/type_station/editables/link_finder.js.coffee +26 -26
- data/app/assets/javascripts/type_station/editables/text_html_editor.js.coffee +63 -0
- data/app/assets/javascripts/type_station/init.js.coffee +8 -50
- data/app/assets/javascripts/type_station/lib/models.js.coffee +5 -5
- data/app/assets/javascripts/type_station/lib/ts.js.coffee +84 -1
- data/app/assets/stylesheets/type_station/admin_bar.css.scss +10 -16
- data/app/assets/stylesheets/type_station/base.css.scss +26 -51
- data/app/controllers/type_station/admin/entities_controller.rb +53 -0
- data/app/controllers/type_station/admin/pages_controller.rb +4 -50
- data/app/controllers/type_station/application_controller.rb +1 -11
- data/app/controllers/type_station/pages_controller.rb +5 -5
- data/app/models/type_station/content.rb +14 -1
- data/app/models/type_station/entity.rb +80 -0
- data/app/models/type_station/page.rb +8 -120
- data/app/presenters/type_station/admin_bar_presenter.rb +9 -0
- data/app/presenters/type_station/content_presenter.rb +12 -2
- data/app/presenters/type_station/page_presenter.rb +6 -39
- data/app/presenters/type_station/presenter.rb +71 -0
- data/app/views/type_station/toolbars/_admin_bar.html.haml +18 -123
- data/config/routes.rb +3 -1
- data/lib/type_station/blocks/base.rb +32 -20
- data/lib/type_station/blocks/entity.rb +56 -0
- data/lib/type_station/blocks/field.rb +34 -0
- data/lib/type_station/concerns/path_generator.rb +69 -0
- data/lib/type_station/concerns/templatable.rb +16 -0
- data/lib/type_station/concerns.rb +2 -0
- data/lib/type_station/configuration.rb +8 -4
- data/lib/type_station/dsl.rb +6 -7
- data/lib/type_station/engine.rb +6 -0
- data/lib/type_station/railtie.rb +3 -5
- data/lib/type_station/version.rb +2 -2
- data/lib/type_station/view_helpers.rb +23 -86
- data/spec/dummy/app/helpers/another_name_helper.rb +1 -1
- data/spec/dummy/app/models/team.rb +3 -0
- data/spec/dummy/app/presenters/index_presenter.rb +18 -0
- data/spec/dummy/app/presenters/team_presenter.rb +7 -0
- data/spec/dummy/app/views/pages/index_other.html.erb +79 -78
- data/spec/dummy/config/initializers/config.rb +3 -3
- data/spec/dummy/config/mongoid.yml +2 -2
- data/spec/dummy/log/development.log +3229 -63846
- data/spec/dummy/tmp/cache/assets/development/sass/{d54c17bdece82ab7e34807e87c417af01a5b0646 → 5195863f2e60bebe518d97f94bb3d4b177b0b425}/chosen.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/631a1227c978d11ae414a23c82459b12296c5574/admin_bar.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/631a1227c978d11ae414a23c82459b12296c5574/base.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/{6fe6492bf5ccb83234e7598f9cf2dee000d6fd1d → d9d756bcf855b539202ff507923feaef562a86d9}/_ionicons-font.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/{6fe6492bf5ccb83234e7598f9cf2dee000d6fd1d → d9d756bcf855b539202ff507923feaef562a86d9}/_ionicons-icons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/{6fe6492bf5ccb83234e7598f9cf2dee000d6fd1d → d9d756bcf855b539202ff507923feaef562a86d9}/_ionicons-variables.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/{6fe6492bf5ccb83234e7598f9cf2dee000d6fd1d → d9d756bcf855b539202ff507923feaef562a86d9}/ionicons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/03faf403247b5c2b01bfc3b194c9a108 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{42a12b3c21e3f05a34cb05b0c63c690c → 04ce8830b8d9eb9869b4e717c72e4c77} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{ffa56cf66ebfb8bbe51b752fa5e854b7 → 05111c950181c5c7db004ab0537ad63a} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{12f3bf6fe9d52ff385ee5f0e1a94e256 → 06f720204faca086942ed12e58cc7e99} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{d11321573db03471f91362fca3d3c5c5 → 06fe7bcbc391b00c5a196a93f90ecabb} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/07a3408841510f4fb1eb0ede33cdb028 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1057bd97781bd01e53c6be1c18815331 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{311ffc36d5a7e1d3216e672b3e57e7ed → 11a2e83f32c39554a1d8d1812fcfefa8} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1e7d8fa3d4680fd1b85b0881f495525f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/21bece9bdb4b2b78355dfc15633484fb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{a37ae28c4ac4e0bc0e2cb4423c40b24c → 268002ee16dde5d8865159003c5b9f56} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{8bec81b5959299e431f5adfc6c410719 → 2b6923a6b24ec5ce8800fedede741151} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2bf3770399e96a6b30d1d1078739c1f0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2e14a727c7ff43dcd32c2d15c7d4e49c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{3030c5140945ca6d88763e4f9136b93d → 2ed9e055cb4bc9748131e083298e90f3} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{5c2d0bbd303da757c44328df9bccedf7 → 2f14bda8290314a0fd6c175fcad55e69} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{985af24e0c117b294f64311bf0187513 → 2fbff71a6bc8ae2a4172de51e568ea3b} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{f566af7037e0e3042b19bd5ee11b21c4 → 305b28bd53201553c837a27834d3fee9} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{c0c85148a1307bb6d6a87139811da3ad → 318672b968af34804563871c8b1e1d23} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{0b495c71c71a2d087e00d9f77c89b537 → 3203d681677ab8bf95858a71dca72ad1} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{4d6d33dd083743a6d1e0d708c4e4e7aa → 333827a9cf72b750448f816161e1ecda} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{0d8f54d1682f575aec8f27993d3af45e → 34c2fc984403383284ed5696d70c010e} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{0c7b9a1778b05de10a66fde42c60b47c → 35191399e8ceb198f89ffa9a8e5e6d18} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{90c324b461b60cdeb6b549dbf932f162 → 3af1f1791459e78c28c4e7e55115168b} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/412cf59ab3b41a364279e5d53ad68152 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{f7345825a6e023e880e9a0744a4896bd → 4440e4eecd7f5abe3a350795b7c562a3} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/48d06b052d87bd96e9e9b8a50a69b18a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4c48023968030daffd8c05e4e1ea78bf +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{7ef9b3c736e553151f79c509136b1c44 → 4d43e505f0233833295b19c57e3f51bb} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{b6cef25e5142cb56f70ca0a8d31f312e → 5374e389194ac8d6d65ecfcfb0659919} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{cf5014041f97c55eee7c67feddf11b81 → 5403ae64c2a793b44bcc6728afa05e81} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/55e73132107a6039a07892904fb85b3f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/57096386bc19c2ad35327c072821d712 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/57a2ad49228767f4bdd4a7d12ab9fdeb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{db1a08b3c680b077476f767a8addb44b → 58db8276645522fa111c4a297a6b3252} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5963d34af3b79a722db1c029191b2127 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{da50fa3c24d736a5eb19bc107acd4ae7 → 5fd77f69e8769780727bcddc3bbef6da} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6527d2ff5eb008ef2a32f7796a6490d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{8438f80ca5340a65edcf7a37bde5d43d → 6694f57666543a152f6f95b772233bd3} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/685c03a878584655eb4475b4cdacdd75 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6aad7e14a81065dffac7093aacf234f2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6c54d2efe05a3f58e5194a546a67d7b3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6e3a8b3faf7a8b9b6491dc8a5c1e8d23 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{f1567fa9088d43b2fb0f9a0caa1ed107 → 6eea7ea5e4bfeeb41ccc235189ffd18b} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{d4248f8b11c6b21abffed4a5e718722d → 722eee3ce416c20905dda68eab3d517e} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{56afaea2e164564be4d5f5bbc7f81050 → 7960910c69f2bcf82925d090b4745c83} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{740af710fe4125080680a8561398cd43 → 7f512b27e66494dd23baf2e5fb34b96a} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{e842f1fb8542889be369b76982f3157c → 80dca6b775b0c8c933ba3bd53864a11f} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/82cfb0f0bcd16b930ec1befc0d5cd8b8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/83c193129690d0c85c5370d822c4da82 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/83d012091edd332e8b52f58cf1a8aba5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/848f8022bef8c0160c31cfa94841c896 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{0d5a85a524a12561b8cfff4e8d06b332 → 85944b215ca6c0d1a1d7806605dfd545} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{570677a99f6abc19d74f793d5cb24f5f → 88c61205fb088e828eaf562152fb80e1} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{849593e0c4cb1f2e2b3f7941be4bfb17 → 895bfeb62235ec6bfdc13f057e975c50} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8a5150409eaf099549443d23d5d53c88 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8db596b50a3235e0956dfbc06ac6c990 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8f4346bc63f61c8274390947394eeeb7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{79b7c5b4127ac19346c56dd224bf8b19 → 8feb7e7adc10eec24e0ab54bef0c0857} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{a87019c62b5fb3b3dc75db921d98ad59 → 95130b18f8c813f4fa587e14f232c241} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{98bae9585542e37b839b68b25937fdf6 → 963c4df95ae6a8d8e54c7fe89d702fff} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/973acef0f44b868ef5c17c4d65d80ab0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{ce5a41536c75e4bc37a5b0be19e9f786 → 98a60691ad77eb6c878a60773f6ca69a} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9a5b201b4dddc98127641bcf852a4ea8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9eafacb2fca5506e20523d97e92f5251 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a0c3540cd0a5390bd8985db285d1574f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a17ca57bada5cb7cb8c1d0b10fbd9904 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{ff96189f6d2677d5fa7c400167c74874 → a1b319a55f70f85dbee50bcb28aa81d4} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{99fd048521a12239746f91aa138613bf → a51f09cb29d7bd6071434928f56887af} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{f084a3a159f23d2f0f9943c4a2e5f8e9 → a6195a184397922f6afd2e7bf3d4a7e7} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a8ecf10c3e4c97b944e10c2cb0931fd5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{0b230f1bf23bd7b1392e34cacde00da0 → aa5deb52794e98d34cc0b868202211ee} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aa9f5420651d522078eadd9de8350d35 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{99a40b6133b9eaa016bd6cf01fff987a → aab1005ca95fedeee05cb5f8d06eaa6c} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{21f725a9b3479e57849414f8272a905b → ab1e4897ca815d147fb473e23bf35bd7} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{a87cf4d10d892fb23f2fc818d7ca7207 → afa9de4377b9615980b737000fc68a2a} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b1fc0c5413b58bf1bf40a04dcd687d18 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b3383c0b0b1d8449e7a9c742a544edb7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b3b877cfd5c63c892cfe8c6248e3d003 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{cad829df95038ddc2958dab7e1b591ad → b401f905161e2ce92571718065d5e35c} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b62bf39167cc044d19958ff731ef6578 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bb1b5b9fd84c67b2b994666f8162e79b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bc7d25832d4cd5332bdf3ec19783ae76 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bcdec54b8cd89a231c20c7c45d8eca71 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{492de35b811aed43fc60de0227a5d0ab → bf1e850366946d51a864f66042acb16d} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c06990d2e8f15c9de915a7b33d17dac5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c136f3c36aea515920f7a51809e79094 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{c0600b832214f9e16294a1c37c0c3f6e → c39ca879cd6484827df2dd7605577066} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c4b181c541556124b2275b0ae4a94461 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c7699aedd27ae409c1ccf0c02a7782fc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{ef7d0257e22920401b54d6662e627252 → c85e062576453cbe6504556843184e48} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ccc931022e80caf6ac008099f2589e7d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{a8e9ef7143431aa34568a350dad82af2 → cf5e51adf346ca56aee15b72f394fce9} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{b6f21db694cacb7f5bcbb93cf45f65ed → cf6bd170bc3f231ca9da6434bd53359b} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cfcaaf334e027f46a4eddee54f3e3b27 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d54b81fedb82c1d520b92cfd20e18ef5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d5de77884a5b0a88b13f76a0d8d2075e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d63daaafe9be33950037e285fb142eb5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ddf237d5e172776d2061090bbf8fc6a5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{e736f7cd2394078039a3b11e6ee42802 → deb9cd21864c66e80861376afc6e17cd} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{d02809f043bce2445374a43a8f64b4b8 → e03864d9763609a4fa6412863f2c9a8c} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e080ee3bf59ec12040e4cabaa97dc241 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e1bc9024b810c879e24aa1de0f013358 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{147a08ea451b98ff5a28a0088b7d5c89 → e348418df158e4447a3120415eaeb3c5} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e39cb3a82453b50e888022543695b4e2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e77bfcad61fabf7ed2d4dd21e881b8c8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{a855b42a37290315d53e93434721869d → ea306a2a3565b22cd16bf45555e751b5} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/eb1b637b8414e22b792fe2f3549283e3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{e8d6c5a2e13578e611f85cd7acd0088f → ebfd318084bf0cbcc2ab7ee6bf5c306c} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{42a7cd7e7e7d73b1882a085a3ed212b5 → ef58d6e4cfadfdd86e4f38c165c82e0a} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f4205d4b499b80a483c15c2408be1874 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{7316e0f7a256fd92f78a590981226fbe → f4a54e3803a4dbd2339782565404ffc1} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{d1e5823c113c9ee03e6c6d7773eb6fac → f7fe63074e55b9b04eef14bac17d6344} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f8a56681cc92b0dba2c2fdb753933a8b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{f14e64162f52a8d2d22ff9e1c1cc4fac → f9c9583263fc196b23e3630ecaca20ef} +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fba3208cb66e48884d72c7875e5ff4ca +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/{6f7174fb7f1fbb8e01d382a128a979e8 → fee315e7956140b77895e8a26e569ca5} +0 -0
- data/spec/dummy/tmp/pids/server.pid +1 -0
- data/vendor/assets/javascripts/medium-editor.js +1919 -1125
- data/vendor/assets/stylesheets/medium-editor/medium-editor.css +39 -9
- data/vendor/assets/stylesheets/medium-editor/themes/bootstrap.css +3 -5
- data/vendor/assets/stylesheets/medium-editor/themes/bootstrap.min.css +1 -1
- data/vendor/assets/stylesheets/medium-editor/themes/default.css +6 -8
- data/vendor/assets/stylesheets/medium-editor/themes/default.min.css +1 -1
- data/vendor/assets/stylesheets/medium-editor/themes/flat.css +2 -2
- data/vendor/assets/stylesheets/medium-editor/themes/mani.css +4 -4
- data/vendor/assets/stylesheets/medium-editor/themes/mani.min.css +1 -1
- data/vendor/assets/stylesheets/medium-editor/themes/roman.css +4 -4
- data/vendor/assets/stylesheets/medium-editor/themes/roman.min.css +1 -1
- metadata +337 -669
- data/app/assets/javascripts/type_station/editables/edit_link.js.coffee +0 -90
- data/app/assets/javascripts/type_station/editables/edit_page.js.coffee +0 -116
- data/app/assets/javascripts/type_station/editables/file.js.coffee +0 -66
- data/app/assets/javascripts/type_station/editables/image.js.coffee +0 -68
- data/app/assets/javascripts/type_station/editables/new_page.js.coffee +0 -79
- data/app/assets/javascripts/type_station/editables/text_html.js.coffee +0 -66
- data/app/views/layouts/type_station/application.html.erb +0 -14
- data/lib/type_station/blocks/edit_link.rb +0 -27
- data/lib/type_station/blocks/edit_page.rb +0 -28
- data/lib/type_station/blocks/editable_file.rb +0 -19
- data/lib/type_station/blocks/editable_html.rb +0 -15
- data/lib/type_station/blocks/editable_image.rb +0 -19
- data/lib/type_station/blocks/editable_text.rb +0 -19
- data/lib/type_station/blocks/new_collection.rb +0 -15
- data/lib/type_station/blocks/new_page.rb +0 -19
- data/lib/type_station/blocks/static_link.rb +0 -31
- data/lib/type_station/blocks/stickable.rb +0 -23
- data/lib/type_station/helpers/edit.rb +0 -36
- data/lib/type_station/helpers/editable.rb +0 -37
- data/lib/type_station/helpers/new.rb +0 -25
- data/lib/type_station/helpers/utilities.rb +0 -40
- data/lib/type_station/helpers.rb +0 -2
- data/spec/dummy/log/test.log +0 -1380
- data/spec/dummy/tmp/cache/assets/development/sass/d51071614a47c21d9e7a31bac67dfe51fed9abe3/_buttons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/d51071614a47c21d9e7a31bac67dfe51fed9abe3/_mixins.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/d51071614a47c21d9e7a31bac67dfe51fed9abe3/_variables.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_alerts.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_background-variant.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_border-radius.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_buttons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_center-block.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_clearfix.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_forms.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_gradients.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_grid-framework.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_hide-text.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_image.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_labels.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_list-group.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_nav-divider.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_nav-vertical-align.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_opacity.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_pagination.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_panels.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_progress-bar.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_reset-filter.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_resize.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_responsive-visibility.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_size.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_tab-focus.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_table-row.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_text-emphasis.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_text-overflow.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e63a0fef5d60e74f917815e496f07ccb348b5adc/_vendor-prefixes.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/fde2fd9fd4415e2893f7c202128d5909c847645f/admin_bar.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/fde2fd9fd4415e2893f7c202128d5909c847645f/base.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/07d82d3c1e3632b7f4bf807c2965e13b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0825425ebf24766be13ebf4e696dc267 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0aa47ff15e22d0e74389460968c32717 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0b163fa81b2ba6f9e0a9d3e6164af7cd +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0cbda04368ddbf6d761e788334075e7c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0dd8d5d73e593c919592d3d02deb62e8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0e846d0579634bd3924d701ebcd09040 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/127cceefef1ada19384ec7fec980c5ee +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/182d8b06dcdb4f799088741f553903d3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1b64a5fd92110dbd078f4ea964e2d0ad +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1d67ea254300e2a208df8f296477fca2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1ff95c0ddc6f6687c044d2a11b8323eb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2155dbeb10a65c2519b49179b3bc6f3a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2535956b4e5e79e8fbd3d30f68de7024 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/28ad54c6e5882957dd269b1b2a3c3c75 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/29a7d8d0307a932153f20c48b57ecda1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2d0ee72828e0dcbaf2390d3d67595461 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2d13dd9465026b66cce912e30e142020 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/300f6f1e08668cacf691691df4d13889 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/30e3d4f790a4234a26c7f74efba15a2e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/33ba18d4294cbb074bd3afee4728e2b9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3571d341c3629bb4c9034d2f924f34ac +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3a3d03c0a92f21419e1e85991774a28b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3dea6de9e06b1e372ebf4790f5eecc48 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3e7667a45f81a73030337cb8afe0acc3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4144b15d96ec80b5c7fb367b57aaeef3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/43c1381ebee89f30d22f80f305f78ee6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/450d192e072fd68fb5c20711bdd21c9e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4729c7f6ab6a91a079ab04a8f04e932b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/48f052ab53e11ca42b15cc7f40fe2d4c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4c324a18fe6bf7dbf7a04cccbfdb366e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4fc791a177c880f50bf425e8d2862c20 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/51c578f550b2fd1ff7a873dbcf75b75b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/535f36c50d5b36ff83bc03bc6a19b8fc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/539ac885f1ce2ff4f1d693223a77b74d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/53ef9dcaf6b6ddcefceec48187797dc5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/55c2f55c27a62b998b8d33555158da53 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/575f0b5d245887cf587b0169d6993308 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/579b1a727fe5b55dfe450b0835c56a65 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5a806147b739da644a296d2def76c243 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5d79ea654842f86bec8702b02616c633 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/60b14e8e4c0d91f7af9d162cf4c67a9c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/61d7a0e99e49f7a9917d385223a97015 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/623a22125c47f6f4391fd1a56e40b8a0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/65cf3864264ab5fd70d6e74a934adf8d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6b734dd1ce043b913eb812a20ea2d42f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/6e0fcc860289a427d98aa4ee86bd12b7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/709e51f7191f25018b0012df4ff6389f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/70dfe9a7b27467587b0d4d00a7e0f6fb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/71e10ee69b177ab54cb6033385a77ef9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/735d50df5dc976a13b56aa81f5970298 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/73939a29e193b5a1fad031fa75f94119 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/73cc8ce397fbfe4408bcac87302a6952 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7cfbbb15a3f9dcb4a5ee988ea309504d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7efe4acab136927c0b1df06d7c1212cd +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7f14b0b39c11df4f58e03d390b589547 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7fc9c7161658070c78790a28b4ff590f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/825bd4dba50f1ea2c0ae5e8a3708119e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/84822f25094f67b515ad5f452381dcb0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/85c54085eea77d703dd054b1adcf309d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/85d858ed64f06543161931735cb77748 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/85fe1c52386ac4419fdf124a22228a98 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8803d0a63bbf3fce683c968d4a900480 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/887e2151ef4cd6e915fd9bfc27c41d7d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8b33a159429f95c8fbb14e5d45b01967 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8caeabedf4518243187dbf20bf76a0d8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8e60a95e73cedfb4b58fbf6fc32564b3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/92c1d07e65b1251e6a9f62064f749658 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/92d59399e3f951cc11721f03a2b328fd +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/966db607ae0e7aad72cf171b8fec537a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9a9290586c54792a989ea679f38ebc98 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9c6580777be29320c993da37d292fcae +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9c8cd77db76e28f0a074573a72509ce9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9d5a258ab9e431a1a7eaf931ed4c3fb3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9d961151482903524be9a51e312c8d49 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a165e7cd7871ea0af12d29ef10193272 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a2b3a282d5d8a5c20444d1a27f70a61e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a539d3e91d5fb8eeb973ac8706bbf5c9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ab4192212bd78e95967e5a716548236f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/acbb40a3fca4d574f84a89894274b5ab +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/af97ed366de5933460d41b9198ddc99e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/afad1821c4be2be037ae8672c21db00a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b0acb9e437685aa3f0c0a2ca751a04ce +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b0b0cf6bca2dad4b85f30801d615eac4 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b1a982f644c0350cb843b63d69a9506f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b772142455a4c78005e67264e239e867 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b85e6623f6fed5c83a7a375ff15bdd79 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b89a0282f37737eb86781a8ddbdf14a3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bc2f1083a88e2e7c4e88f13f025824ed +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bcf0359847dcb2f9179c6a46248bcea5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bd3113e1418cbc640eb6e1b06d486163 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c4e5a8fece2f70f43c6e7cc4a790def7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c4ec64020d1ff391bab8c2f0bbc7cd02 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c501910d385d42ea0cf75ae3c4f220e6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c7628a8a59ea560abb9213543333222c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cb80f61fc678a70e99e16156ae6e07d1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cc9a378dda4e65e86b1f3fd522d019a1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cdaf4cc6f0288db4f6fa5b8073ac6272 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cdedc95319572f0c3e36a3a5a044572d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d05d272f053261f2d5e3dcca85f19172 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d05d2c6ed489a61e2a9756430af9f48f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d07ab5a5474c0fb71b3bd351947707e5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d1f0a2d6fd58a203f3ca66950c822c33 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d3a2586089547d98e033029b94eb86a5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d9924e9691068558c4ce3a20bec7fea1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/da9349ff5720f218552184c8fef40d7a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/dd9f84cc20227ad66bbd1f26bbc11ccc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/de9d72cab443cce32e1d45e62d2b4c04 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e03c578b73cf9936edc84ff2b48c07da +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e04691fd3eaee6408ac1d1b7fade69e6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e40f49d676971d013ad991bdf2dc344b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e72700144fea9ebdc4813bc00f023fc7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e9b5bc7c65d160f7e31993bbe7a05d63 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ebdd71d1c3cf34d7d174e5c1abb123b7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ec22367449630f093cf0449cea0abdfd +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f479b0a1614f114b61a63664e172dec1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f6eeeb889d424f50b0f725d4656e0721 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7c0e18cf77ef251a27e9ff91578fb47 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fa8cab89937bf25aa48b23445f1b4f64 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fc5de94b46d8aff03ec5355f912a9a81 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fd1cae0417fea68172ab7b747a1d6fd8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fd2dacd118d6197c3a9325e2103abe7d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fe43b3c0b7d3233a250ce9c46342919c +0 -0
- data/spec/dummy/tmp/cache/assets/test/sass/fde2fd9fd4415e2893f7c202128d5909c847645f/admin_bar.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/test/sass/fde2fd9fd4415e2893f7c202128d5909c847645f/base.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/0825425ebf24766be13ebf4e696dc267 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/0b495c71c71a2d087e00d9f77c89b537 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/0cbda04368ddbf6d761e788334075e7c +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/0d8f54d1682f575aec8f27993d3af45e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/12f3bf6fe9d52ff385ee5f0e1a94e256 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/182d8b06dcdb4f799088741f553903d3 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/1b64a5fd92110dbd078f4ea964e2d0ad +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/2155dbeb10a65c2519b49179b3bc6f3a +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/21f725a9b3479e57849414f8272a905b +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/2535956b4e5e79e8fbd3d30f68de7024 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/28ad54c6e5882957dd269b1b2a3c3c75 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/29a7d8d0307a932153f20c48b57ecda1 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/2d13dd9465026b66cce912e30e142020 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/300f6f1e08668cacf691691df4d13889 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/311ffc36d5a7e1d3216e672b3e57e7ed +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/3a3d03c0a92f21419e1e85991774a28b +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/3dea6de9e06b1e372ebf4790f5eecc48 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/4144b15d96ec80b5c7fb367b57aaeef3 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/4729c7f6ab6a91a079ab04a8f04e932b +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/4c324a18fe6bf7dbf7a04cccbfdb366e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/535f36c50d5b36ff83bc03bc6a19b8fc +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/55c2f55c27a62b998b8d33555158da53 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/56afaea2e164564be4d5f5bbc7f81050 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/570677a99f6abc19d74f793d5cb24f5f +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/579b1a727fe5b55dfe450b0835c56a65 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/6f7174fb7f1fbb8e01d382a128a979e8 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/735d50df5dc976a13b56aa81f5970298 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/7746e5b6582f701e40ad5ae69e7a6d25 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/7f14b0b39c11df4f58e03d390b589547 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/8091ad5bff4f0d3d70f874750430241b +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/825bd4dba50f1ea2c0ae5e8a3708119e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/849593e0c4cb1f2e2b3f7941be4bfb17 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/85d858ed64f06543161931735cb77748 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/8803d0a63bbf3fce683c968d4a900480 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/887e2151ef4cd6e915fd9bfc27c41d7d +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/8bec81b5959299e431f5adfc6c410719 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/90c324b461b60cdeb6b549dbf932f162 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/92d59399e3f951cc11721f03a2b328fd +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/966db607ae0e7aad72cf171b8fec537a +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/98bae9585542e37b839b68b25937fdf6 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/99fd048521a12239746f91aa138613bf +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/a539d3e91d5fb8eeb973ac8706bbf5c9 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/a87019c62b5fb3b3dc75db921d98ad59 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/a87cf4d10d892fb23f2fc818d7ca7207 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/ab4192212bd78e95967e5a716548236f +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/af97ed366de5933460d41b9198ddc99e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/b6cef25e5142cb56f70ca0a8d31f312e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/b6f21db694cacb7f5bcbb93cf45f65ed +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/bd3113e1418cbc640eb6e1b06d486163 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/c0600b832214f9e16294a1c37c0c3f6e +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/cad829df95038ddc2958dab7e1b591ad +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d05d272f053261f2d5e3dcca85f19172 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d1e5823c113c9ee03e6c6d7773eb6fac +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d1f0a2d6fd58a203f3ca66950c822c33 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d3a2586089547d98e033029b94eb86a5 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d4248f8b11c6b21abffed4a5e718722d +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/da50fa3c24d736a5eb19bc107acd4ae7 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/db1a08b3c680b077476f767a8addb44b +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/dd9f84cc20227ad66bbd1f26bbc11ccc +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/de9d72cab443cce32e1d45e62d2b4c04 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/e03c578b73cf9936edc84ff2b48c07da +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/e842f1fb8542889be369b76982f3157c +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/ebdd71d1c3cf34d7d174e5c1abb123b7 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/f14e64162f52a8d2d22ff9e1c1cc4fac +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/f566af7037e0e3042b19bd5ee11b21c4 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/test/sprockets/fd1cae0417fea68172ab7b747a1d6fd8 +0 -0
|
@@ -1,253 +1,1399 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/*global self, document, DOMException */
|
|
2
|
+
|
|
3
|
+
/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
|
|
4
|
+
|
|
5
|
+
// Full polyfill for browsers with no classList support
|
|
6
|
+
if (!("classList" in document.createElement("_"))) {
|
|
7
|
+
(function (view) {
|
|
8
|
+
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
if (!('Element' in view)) return;
|
|
12
|
+
|
|
13
|
+
var
|
|
14
|
+
classListProp = "classList"
|
|
15
|
+
, protoProp = "prototype"
|
|
16
|
+
, elemCtrProto = view.Element[protoProp]
|
|
17
|
+
, objCtr = Object
|
|
18
|
+
, strTrim = String[protoProp].trim || function () {
|
|
19
|
+
return this.replace(/^\s+|\s+$/g, "");
|
|
20
|
+
}
|
|
21
|
+
, arrIndexOf = Array[protoProp].indexOf || function (item) {
|
|
22
|
+
var
|
|
23
|
+
i = 0
|
|
24
|
+
, len = this.length
|
|
25
|
+
;
|
|
26
|
+
for (; i < len; i++) {
|
|
27
|
+
if (i in this && this[i] === item) {
|
|
28
|
+
return i;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return -1;
|
|
32
|
+
}
|
|
33
|
+
// Vendors: please allow content code to instantiate DOMExceptions
|
|
34
|
+
, DOMEx = function (type, message) {
|
|
35
|
+
this.name = type;
|
|
36
|
+
this.code = DOMException[type];
|
|
37
|
+
this.message = message;
|
|
38
|
+
}
|
|
39
|
+
, checkTokenAndGetIndex = function (classList, token) {
|
|
40
|
+
if (token === "") {
|
|
41
|
+
throw new DOMEx(
|
|
42
|
+
"SYNTAX_ERR"
|
|
43
|
+
, "An invalid or illegal string was specified"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (/\s/.test(token)) {
|
|
47
|
+
throw new DOMEx(
|
|
48
|
+
"INVALID_CHARACTER_ERR"
|
|
49
|
+
, "String contains an invalid character"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return arrIndexOf.call(classList, token);
|
|
53
|
+
}
|
|
54
|
+
, ClassList = function (elem) {
|
|
55
|
+
var
|
|
56
|
+
trimmedClasses = strTrim.call(elem.getAttribute("class") || "")
|
|
57
|
+
, classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
|
|
58
|
+
, i = 0
|
|
59
|
+
, len = classes.length
|
|
60
|
+
;
|
|
61
|
+
for (; i < len; i++) {
|
|
62
|
+
this.push(classes[i]);
|
|
63
|
+
}
|
|
64
|
+
this._updateClassName = function () {
|
|
65
|
+
elem.setAttribute("class", this.toString());
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
, classListProto = ClassList[protoProp] = []
|
|
69
|
+
, classListGetter = function () {
|
|
70
|
+
return new ClassList(this);
|
|
71
|
+
}
|
|
72
|
+
;
|
|
73
|
+
// Most DOMException implementations don't allow calling DOMException's toString()
|
|
74
|
+
// on non-DOMExceptions. Error's toString() is sufficient here.
|
|
75
|
+
DOMEx[protoProp] = Error[protoProp];
|
|
76
|
+
classListProto.item = function (i) {
|
|
77
|
+
return this[i] || null;
|
|
78
|
+
};
|
|
79
|
+
classListProto.contains = function (token) {
|
|
80
|
+
token += "";
|
|
81
|
+
return checkTokenAndGetIndex(this, token) !== -1;
|
|
82
|
+
};
|
|
83
|
+
classListProto.add = function () {
|
|
84
|
+
var
|
|
85
|
+
tokens = arguments
|
|
86
|
+
, i = 0
|
|
87
|
+
, l = tokens.length
|
|
88
|
+
, token
|
|
89
|
+
, updated = false
|
|
90
|
+
;
|
|
91
|
+
do {
|
|
92
|
+
token = tokens[i] + "";
|
|
93
|
+
if (checkTokenAndGetIndex(this, token) === -1) {
|
|
94
|
+
this.push(token);
|
|
95
|
+
updated = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
while (++i < l);
|
|
99
|
+
|
|
100
|
+
if (updated) {
|
|
101
|
+
this._updateClassName();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
classListProto.remove = function () {
|
|
105
|
+
var
|
|
106
|
+
tokens = arguments
|
|
107
|
+
, i = 0
|
|
108
|
+
, l = tokens.length
|
|
109
|
+
, token
|
|
110
|
+
, updated = false
|
|
111
|
+
, index
|
|
112
|
+
;
|
|
113
|
+
do {
|
|
114
|
+
token = tokens[i] + "";
|
|
115
|
+
index = checkTokenAndGetIndex(this, token);
|
|
116
|
+
while (index !== -1) {
|
|
117
|
+
this.splice(index, 1);
|
|
118
|
+
updated = true;
|
|
119
|
+
index = checkTokenAndGetIndex(this, token);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
while (++i < l);
|
|
123
|
+
|
|
124
|
+
if (updated) {
|
|
125
|
+
this._updateClassName();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
classListProto.toggle = function (token, force) {
|
|
129
|
+
token += "";
|
|
130
|
+
|
|
131
|
+
var
|
|
132
|
+
result = this.contains(token)
|
|
133
|
+
, method = result ?
|
|
134
|
+
force !== true && "remove"
|
|
135
|
+
:
|
|
136
|
+
force !== false && "add"
|
|
137
|
+
;
|
|
138
|
+
|
|
139
|
+
if (method) {
|
|
140
|
+
this[method](token);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (force === true || force === false) {
|
|
144
|
+
return force;
|
|
145
|
+
} else {
|
|
146
|
+
return !result;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
classListProto.toString = function () {
|
|
150
|
+
return this.join(" ");
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (objCtr.defineProperty) {
|
|
154
|
+
var classListPropDesc = {
|
|
155
|
+
get: classListGetter
|
|
156
|
+
, enumerable: true
|
|
157
|
+
, configurable: true
|
|
158
|
+
};
|
|
159
|
+
try {
|
|
160
|
+
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
|
161
|
+
} catch (ex) { // IE 8 doesn't support enumerable:true
|
|
162
|
+
if (ex.number === -0x7FF5EC54) {
|
|
163
|
+
classListPropDesc.enumerable = false;
|
|
164
|
+
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else if (objCtr[protoProp].__defineGetter__) {
|
|
168
|
+
elemCtrProto.__defineGetter__(classListProp, classListGetter);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}(self));
|
|
4
172
|
}
|
|
5
173
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
define
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
174
|
+
(function (root, factory) {
|
|
175
|
+
'use strict';
|
|
176
|
+
if (typeof module === 'object') {
|
|
177
|
+
module.exports = factory;
|
|
178
|
+
} else if (typeof define === 'function' && define.amd) {
|
|
179
|
+
define(function () {
|
|
180
|
+
return factory;
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
root.MediumEditor = factory;
|
|
184
|
+
}
|
|
185
|
+
}(this, function () {
|
|
186
|
+
|
|
187
|
+
'use strict';
|
|
188
|
+
|
|
189
|
+
var Util;
|
|
190
|
+
|
|
191
|
+
(function (window, document) {
|
|
192
|
+
'use strict';
|
|
193
|
+
|
|
194
|
+
function copyInto(dest, source, overwrite) {
|
|
195
|
+
var prop;
|
|
196
|
+
dest = dest || {};
|
|
197
|
+
for (prop in source) {
|
|
198
|
+
if (source.hasOwnProperty(prop) && (overwrite || dest.hasOwnProperty(prop) === false)) {
|
|
199
|
+
dest[prop] = source[prop];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return dest;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
Util = {
|
|
206
|
+
|
|
207
|
+
// http://stackoverflow.com/questions/17907445/how-to-detect-ie11#comment30165888_17907562
|
|
208
|
+
// by rg89
|
|
209
|
+
isIE: ((navigator.appName === 'Microsoft Internet Explorer') || ((navigator.appName === 'Netscape') && (new RegExp('Trident/.*rv:([0-9]{1,}[.0-9]{0,})').exec(navigator.userAgent) !== null))),
|
|
210
|
+
|
|
211
|
+
// https://github.com/jashkenas/underscore
|
|
212
|
+
keyCode: {
|
|
213
|
+
BACKSPACE: 8,
|
|
214
|
+
TAB: 9,
|
|
215
|
+
ENTER: 13,
|
|
216
|
+
ESCAPE: 27,
|
|
217
|
+
SPACE: 32,
|
|
218
|
+
DELETE: 46
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
parentElements: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'],
|
|
222
|
+
|
|
223
|
+
defaults: function defaults(dest, source) {
|
|
224
|
+
return copyInto(dest, source);
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
extend: function extend(dest, source) {
|
|
228
|
+
return copyInto(dest, source, true);
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
derives: function derives(base, derived) {
|
|
232
|
+
var origPrototype = derived.prototype;
|
|
233
|
+
function Proto() { }
|
|
234
|
+
Proto.prototype = base.prototype;
|
|
235
|
+
derived.prototype = new Proto();
|
|
236
|
+
derived.prototype.constructor = base;
|
|
237
|
+
derived.prototype = copyInto(derived.prototype, origPrototype);
|
|
238
|
+
return derived;
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// Find the next node in the DOM tree that represents any text that is being
|
|
242
|
+
// displayed directly next to the targetNode (passed as an argument)
|
|
243
|
+
// Text that appears directly next to the current node can be:
|
|
244
|
+
// - A sibling text node
|
|
245
|
+
// - A descendant of a sibling element
|
|
246
|
+
// - A sibling text node of an ancestor
|
|
247
|
+
// - A descendant of a sibling element of an ancestor
|
|
248
|
+
findAdjacentTextNodeWithContent: function findAdjacentTextNodeWithContent(rootNode, targetNode, ownerDocument) {
|
|
249
|
+
var pastTarget = false,
|
|
250
|
+
nextNode,
|
|
251
|
+
nodeIterator = ownerDocument.createNodeIterator(rootNode, NodeFilter.SHOW_TEXT, null, false);
|
|
252
|
+
|
|
253
|
+
// Use a native NodeIterator to iterate over all the text nodes that are descendants
|
|
254
|
+
// of the rootNode. Once past the targetNode, choose the first non-empty text node
|
|
255
|
+
nextNode = nodeIterator.nextNode();
|
|
256
|
+
while (nextNode) {
|
|
257
|
+
if (nextNode === targetNode) {
|
|
258
|
+
pastTarget = true;
|
|
259
|
+
} else if (pastTarget) {
|
|
260
|
+
if (nextNode.nodeType === 3 && nextNode.nodeValue && nextNode.nodeValue.trim().length > 0) {
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
nextNode = nodeIterator.nextNode();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return nextNode;
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
isDescendant: function isDescendant(parent, child) {
|
|
271
|
+
if (!parent || !child) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
var node = child.parentNode;
|
|
275
|
+
while (node !== null) {
|
|
276
|
+
if (node === parent) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
node = node.parentNode;
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
// https://github.com/jashkenas/underscore
|
|
285
|
+
isElement: function isElement(obj) {
|
|
286
|
+
return !!(obj && obj.nodeType === 1);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
now: function now() {
|
|
290
|
+
return Date.now || new Date().getTime();
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// https://github.com/jashkenas/underscore
|
|
294
|
+
throttle: function throttle(func, wait) {
|
|
295
|
+
var THROTTLE_INTERVAL = 50,
|
|
296
|
+
context,
|
|
297
|
+
args,
|
|
298
|
+
result,
|
|
299
|
+
timeout = null,
|
|
300
|
+
previous = 0,
|
|
301
|
+
later;
|
|
302
|
+
|
|
303
|
+
if (!wait && wait !== 0) {
|
|
304
|
+
wait = THROTTLE_INTERVAL;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
later = function () {
|
|
308
|
+
previous = Util.now();
|
|
309
|
+
timeout = null;
|
|
310
|
+
result = func.apply(context, args);
|
|
311
|
+
if (!timeout) {
|
|
312
|
+
context = args = null;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
return function () {
|
|
317
|
+
var currNow = Util.now(),
|
|
318
|
+
remaining = wait - (currNow - previous);
|
|
319
|
+
context = this;
|
|
320
|
+
args = arguments;
|
|
321
|
+
if (remaining <= 0 || remaining > wait) {
|
|
322
|
+
clearTimeout(timeout);
|
|
323
|
+
timeout = null;
|
|
324
|
+
previous = currNow;
|
|
325
|
+
result = func.apply(context, args);
|
|
326
|
+
if (!timeout) {
|
|
327
|
+
context = args = null;
|
|
328
|
+
}
|
|
329
|
+
} else if (!timeout) {
|
|
330
|
+
timeout = setTimeout(later, remaining);
|
|
331
|
+
}
|
|
332
|
+
return result;
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
traverseUp: function (current, testElementFunction) {
|
|
337
|
+
|
|
338
|
+
do {
|
|
339
|
+
if (current.nodeType === 1) {
|
|
340
|
+
if (testElementFunction(current)) {
|
|
341
|
+
return current;
|
|
342
|
+
}
|
|
343
|
+
// do not traverse upwards past the nearest containing editor
|
|
344
|
+
if (current.getAttribute('data-medium-element')) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
current = current.parentNode;
|
|
350
|
+
} while (current);
|
|
351
|
+
|
|
352
|
+
return false;
|
|
353
|
+
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
htmlEntities: function (str) {
|
|
357
|
+
// converts special characters (like <) into their escaped/encoded values (like <).
|
|
358
|
+
// This allows you to show to display the string without the browser reading it as HTML.
|
|
359
|
+
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
// http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
|
|
363
|
+
insertHTMLCommand: function (doc, html) {
|
|
364
|
+
var selection, range, el, fragment, node, lastNode;
|
|
365
|
+
|
|
366
|
+
if (doc.queryCommandSupported('insertHTML')) {
|
|
367
|
+
try {
|
|
368
|
+
return doc.execCommand('insertHTML', false, html);
|
|
369
|
+
} catch (ignore) {}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
selection = window.getSelection();
|
|
373
|
+
if (selection.getRangeAt && selection.rangeCount) {
|
|
374
|
+
range = selection.getRangeAt(0);
|
|
375
|
+
range.deleteContents();
|
|
376
|
+
|
|
377
|
+
el = doc.createElement("div");
|
|
378
|
+
el.innerHTML = html;
|
|
379
|
+
fragment = doc.createDocumentFragment();
|
|
380
|
+
while (el.firstChild) {
|
|
381
|
+
node = el.firstChild;
|
|
382
|
+
lastNode = fragment.appendChild(node);
|
|
383
|
+
}
|
|
384
|
+
range.insertNode(fragment);
|
|
385
|
+
|
|
386
|
+
// Preserve the selection:
|
|
387
|
+
if (lastNode) {
|
|
388
|
+
range = range.cloneRange();
|
|
389
|
+
range.setStartAfter(lastNode);
|
|
390
|
+
range.collapse(true);
|
|
391
|
+
selection.removeAllRanges();
|
|
392
|
+
selection.addRange(range);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// TODO: not sure if this should be here
|
|
398
|
+
setTargetBlank: function (el) {
|
|
399
|
+
var i;
|
|
400
|
+
if (el.tagName.toLowerCase() === 'a') {
|
|
401
|
+
el.target = '_blank';
|
|
402
|
+
} else {
|
|
403
|
+
el = el.getElementsByTagName('a');
|
|
404
|
+
|
|
405
|
+
for (i = 0; i < el.length; i += 1) {
|
|
406
|
+
el[i].target = '_blank';
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
isListItemChild: function (node) {
|
|
412
|
+
var parentNode = node.parentNode,
|
|
413
|
+
tagName = parentNode.tagName.toLowerCase();
|
|
414
|
+
while (this.parentElements.indexOf(tagName) === -1 && tagName !== 'div') {
|
|
415
|
+
if (tagName === 'li') {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
parentNode = parentNode.parentNode;
|
|
419
|
+
if (parentNode && parentNode.tagName) {
|
|
420
|
+
tagName = parentNode.tagName.toLowerCase();
|
|
421
|
+
} else {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
}(window, document));
|
|
429
|
+
|
|
430
|
+
var Selection;
|
|
431
|
+
|
|
432
|
+
(function (window, document) {
|
|
433
|
+
'use strict';
|
|
434
|
+
|
|
435
|
+
Selection = {
|
|
436
|
+
// http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi
|
|
437
|
+
// by You
|
|
438
|
+
getSelectionStart: function (ownerDocument) {
|
|
439
|
+
var node = ownerDocument.getSelection().anchorNode,
|
|
440
|
+
startNode = (node && node.nodeType === 3 ? node.parentNode : node);
|
|
441
|
+
return startNode;
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
findMatchingSelectionParent: function (testElementFunction, contentWindow) {
|
|
445
|
+
var selection = contentWindow.getSelection(), range, current;
|
|
446
|
+
|
|
447
|
+
if (selection.rangeCount === 0) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
range = selection.getRangeAt(0);
|
|
452
|
+
current = range.commonAncestorContainer;
|
|
453
|
+
|
|
454
|
+
return Util.traverseUp(current, testElementFunction);
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
getSelectionElement: function (contentWindow) {
|
|
458
|
+
return this.findMatchingSelectionParent(function (el) {
|
|
459
|
+
return el.getAttribute('data-medium-element');
|
|
460
|
+
}, contentWindow);
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
selectionInContentEditableFalse: function (contentWindow) {
|
|
464
|
+
return this.findMatchingSelectionParent(function (el) {
|
|
465
|
+
return (el && el.nodeName !== '#text' && el.getAttribute('contenteditable') === 'false');
|
|
466
|
+
}, contentWindow);
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
// http://stackoverflow.com/questions/4176923/html-of-selected-text
|
|
470
|
+
// by Tim Down
|
|
471
|
+
getSelectionHtml: function getSelectionHtml() {
|
|
472
|
+
var i,
|
|
473
|
+
html = '',
|
|
474
|
+
sel,
|
|
475
|
+
len,
|
|
476
|
+
container;
|
|
477
|
+
if (this.options.contentWindow.getSelection !== undefined) {
|
|
478
|
+
sel = this.options.contentWindow.getSelection();
|
|
479
|
+
if (sel.rangeCount) {
|
|
480
|
+
container = this.options.ownerDocument.createElement('div');
|
|
481
|
+
for (i = 0, len = sel.rangeCount; i < len; i += 1) {
|
|
482
|
+
container.appendChild(sel.getRangeAt(i).cloneContents());
|
|
483
|
+
}
|
|
484
|
+
html = container.innerHTML;
|
|
485
|
+
}
|
|
486
|
+
} else if (this.options.ownerDocument.selection !== undefined) {
|
|
487
|
+
if (this.options.ownerDocument.selection.type === 'Text') {
|
|
488
|
+
html = this.options.ownerDocument.selection.createRange().htmlText;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return html;
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Find the caret position within an element irrespective of any inline tags it may contain.
|
|
496
|
+
*
|
|
497
|
+
* @param {DOMElement} An element containing the cursor to find offsets relative to.
|
|
498
|
+
* @param {Range} A Range representing cursor position. Will window.getSelection if none is passed.
|
|
499
|
+
* @return {Object} 'left' and 'right' attributes contain offsets from begining and end of Element
|
|
500
|
+
*/
|
|
501
|
+
getCaretOffsets: function getCaretOffsets(element, range) {
|
|
502
|
+
var preCaretRange, postCaretRange;
|
|
503
|
+
|
|
504
|
+
if (!range) {
|
|
505
|
+
range = window.getSelection().getRangeAt(0);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
preCaretRange = range.cloneRange();
|
|
509
|
+
postCaretRange = range.cloneRange();
|
|
510
|
+
|
|
511
|
+
preCaretRange.selectNodeContents(element);
|
|
512
|
+
preCaretRange.setEnd(range.endContainer, range.endOffset);
|
|
513
|
+
|
|
514
|
+
postCaretRange.selectNodeContents(element);
|
|
515
|
+
postCaretRange.setStart(range.endContainer, range.endOffset);
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
left: preCaretRange.toString().length,
|
|
519
|
+
right: postCaretRange.toString().length
|
|
520
|
+
};
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
// http://stackoverflow.com/questions/15867542/range-object-get-selection-parent-node-chrome-vs-firefox
|
|
524
|
+
rangeSelectsSingleNode: function (range) {
|
|
525
|
+
var startNode = range.startContainer;
|
|
526
|
+
return startNode === range.endContainer &&
|
|
527
|
+
startNode.hasChildNodes() &&
|
|
528
|
+
range.endOffset === range.startOffset + 1;
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
getSelectedParentElement: function (range) {
|
|
532
|
+
var selectedParentElement = null;
|
|
533
|
+
if (this.rangeSelectsSingleNode(range) && range.startContainer.childNodes[range.startOffset].nodeType !== 3) {
|
|
534
|
+
selectedParentElement = range.startContainer.childNodes[range.startOffset];
|
|
535
|
+
} else if (range.startContainer.nodeType === 3) {
|
|
536
|
+
selectedParentElement = range.startContainer.parentNode;
|
|
537
|
+
} else {
|
|
538
|
+
selectedParentElement = range.startContainer;
|
|
539
|
+
}
|
|
540
|
+
return selectedParentElement;
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
getSelectionData: function (el) {
|
|
544
|
+
var tagName;
|
|
545
|
+
|
|
546
|
+
if (el && el.tagName) {
|
|
547
|
+
tagName = el.tagName.toLowerCase();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
while (el && Util.parentElements.indexOf(tagName) === -1) {
|
|
551
|
+
el = el.parentNode;
|
|
552
|
+
if (el && el.tagName) {
|
|
553
|
+
tagName = el.tagName.toLowerCase();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
el: el,
|
|
559
|
+
tagName: tagName
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
}(document, window));
|
|
564
|
+
|
|
565
|
+
var DefaultButton,
|
|
566
|
+
ButtonsData;
|
|
567
|
+
|
|
568
|
+
(function (window, document) {
|
|
569
|
+
'use strict';
|
|
570
|
+
|
|
571
|
+
ButtonsData = {
|
|
572
|
+
'bold': {
|
|
573
|
+
name: 'bold',
|
|
574
|
+
action: 'bold',
|
|
575
|
+
aria: 'bold',
|
|
576
|
+
tagNames: ['b', 'strong'],
|
|
577
|
+
style: {
|
|
578
|
+
prop: 'font-weight',
|
|
579
|
+
value: '700|bold'
|
|
580
|
+
},
|
|
581
|
+
useQueryState: true,
|
|
582
|
+
contentDefault: '<b>B</b>',
|
|
583
|
+
contentFA: '<i class="fa fa-bold"></i>',
|
|
584
|
+
key: 'b'
|
|
585
|
+
},
|
|
586
|
+
'italic': {
|
|
587
|
+
name: 'italic',
|
|
588
|
+
action: 'italic',
|
|
589
|
+
aria: 'italic',
|
|
590
|
+
tagNames: ['i', 'em'],
|
|
591
|
+
style: {
|
|
592
|
+
prop: 'font-style',
|
|
593
|
+
value: 'italic'
|
|
594
|
+
},
|
|
595
|
+
useQueryState: true,
|
|
596
|
+
contentDefault: '<b><i>I</i></b>',
|
|
597
|
+
contentFA: '<i class="fa fa-italic"></i>',
|
|
598
|
+
key: 'i'
|
|
599
|
+
},
|
|
600
|
+
'underline': {
|
|
601
|
+
name: 'underline',
|
|
602
|
+
action: 'underline',
|
|
603
|
+
aria: 'underline',
|
|
604
|
+
tagNames: ['u'],
|
|
605
|
+
style: {
|
|
606
|
+
prop: 'text-decoration',
|
|
607
|
+
value: 'underline'
|
|
608
|
+
},
|
|
609
|
+
useQueryState: true,
|
|
610
|
+
contentDefault: '<b><u>U</u></b>',
|
|
611
|
+
contentFA: '<i class="fa fa-underline"></i>',
|
|
612
|
+
key: 'u'
|
|
613
|
+
},
|
|
614
|
+
'strikethrough': {
|
|
615
|
+
name: 'strikethrough',
|
|
616
|
+
action: 'strikethrough',
|
|
617
|
+
aria: 'strike through',
|
|
618
|
+
tagNames: ['strike'],
|
|
619
|
+
style: {
|
|
620
|
+
prop: 'text-decoration',
|
|
621
|
+
value: 'line-through'
|
|
622
|
+
},
|
|
623
|
+
useQueryState: true,
|
|
624
|
+
contentDefault: '<s>A</s>',
|
|
625
|
+
contentFA: '<i class="fa fa-strikethrough"></i>'
|
|
626
|
+
},
|
|
627
|
+
'superscript': {
|
|
628
|
+
name: 'superscript',
|
|
629
|
+
action: 'superscript',
|
|
630
|
+
aria: 'superscript',
|
|
631
|
+
tagNames: ['sup'],
|
|
632
|
+
/* firefox doesn't behave the way we want it to, so we CAN'T use queryCommandState for superscript
|
|
633
|
+
https://github.com/guardian/scribe/blob/master/BROWSERINCONSISTENCIES.md#documentquerycommandstate */
|
|
634
|
+
// useQueryState: true
|
|
635
|
+
contentDefault: '<b>x<sup>1</sup></b>',
|
|
636
|
+
contentFA: '<i class="fa fa-superscript"></i>'
|
|
637
|
+
},
|
|
638
|
+
'subscript': {
|
|
639
|
+
name: 'subscript',
|
|
640
|
+
action: 'subscript',
|
|
641
|
+
aria: 'subscript',
|
|
642
|
+
tagNames: ['sub'],
|
|
643
|
+
/* firefox doesn't behave the way we want it to, so we CAN'T use queryCommandState for subscript
|
|
644
|
+
https://github.com/guardian/scribe/blob/master/BROWSERINCONSISTENCIES.md#documentquerycommandstate */
|
|
645
|
+
// useQueryState: true
|
|
646
|
+
contentDefault: '<b>x<sub>1</sub></b>',
|
|
647
|
+
contentFA: '<i class="fa fa-subscript"></i>'
|
|
648
|
+
},
|
|
649
|
+
'image': {
|
|
650
|
+
name: 'image',
|
|
651
|
+
action: 'image',
|
|
652
|
+
aria: 'image',
|
|
653
|
+
tagNames: ['img'],
|
|
654
|
+
contentDefault: '<b>image</b>',
|
|
655
|
+
contentFA: '<i class="fa fa-picture-o"></i>'
|
|
656
|
+
},
|
|
657
|
+
'quote': {
|
|
658
|
+
name: 'quote',
|
|
659
|
+
action: 'append-blockquote',
|
|
660
|
+
aria: 'blockquote',
|
|
661
|
+
tagNames: ['blockquote'],
|
|
662
|
+
contentDefault: '<b>“</b>',
|
|
663
|
+
contentFA: '<i class="fa fa-quote-right"></i>'
|
|
664
|
+
},
|
|
665
|
+
'orderedlist': {
|
|
666
|
+
name: 'orderedlist',
|
|
667
|
+
action: 'insertorderedlist',
|
|
668
|
+
aria: 'ordered list',
|
|
669
|
+
tagNames: ['ol'],
|
|
670
|
+
useQueryState: true,
|
|
671
|
+
contentDefault: '<b>1.</b>',
|
|
672
|
+
contentFA: '<i class="fa fa-list-ol"></i>'
|
|
673
|
+
},
|
|
674
|
+
'unorderedlist': {
|
|
675
|
+
name: 'unorderedlist',
|
|
676
|
+
action: 'insertunorderedlist',
|
|
677
|
+
aria: 'unordered list',
|
|
678
|
+
tagNames: ['ul'],
|
|
679
|
+
useQueryState: true,
|
|
680
|
+
contentDefault: '<b>•</b>',
|
|
681
|
+
contentFA: '<i class="fa fa-list-ul"></i>'
|
|
682
|
+
},
|
|
683
|
+
'pre': {
|
|
684
|
+
name: 'pre',
|
|
685
|
+
action: 'append-pre',
|
|
686
|
+
aria: 'preformatted text',
|
|
687
|
+
tagNames: ['pre'],
|
|
688
|
+
contentDefault: '<b>0101</b>',
|
|
689
|
+
contentFA: '<i class="fa fa-code fa-lg"></i>'
|
|
690
|
+
},
|
|
691
|
+
'indent': {
|
|
692
|
+
name: 'indent',
|
|
693
|
+
action: 'indent',
|
|
694
|
+
aria: 'indent',
|
|
695
|
+
tagNames: [],
|
|
696
|
+
contentDefault: '<b>→</b>',
|
|
697
|
+
contentFA: '<i class="fa fa-indent"></i>'
|
|
698
|
+
},
|
|
699
|
+
'outdent': {
|
|
700
|
+
name: 'outdent',
|
|
701
|
+
action: 'outdent',
|
|
702
|
+
aria: 'outdent',
|
|
703
|
+
tagNames: [],
|
|
704
|
+
contentDefault: '<b>←</b>',
|
|
705
|
+
contentFA: '<i class="fa fa-outdent"></i>'
|
|
706
|
+
},
|
|
707
|
+
'justifyCenter': {
|
|
708
|
+
name: 'justifyCenter',
|
|
709
|
+
action: 'justifyCenter',
|
|
710
|
+
aria: 'center justify',
|
|
711
|
+
tagNames: [],
|
|
712
|
+
style: {
|
|
713
|
+
prop: 'text-align',
|
|
714
|
+
value: 'center'
|
|
715
|
+
},
|
|
716
|
+
useQueryState: true,
|
|
717
|
+
contentDefault: '<b>C</b>',
|
|
718
|
+
contentFA: '<i class="fa fa-align-center"></i>'
|
|
719
|
+
},
|
|
720
|
+
'justifyFull': {
|
|
721
|
+
name: 'justifyFull',
|
|
722
|
+
action: 'justifyFull',
|
|
723
|
+
aria: 'full justify',
|
|
724
|
+
tagNames: [],
|
|
725
|
+
style: {
|
|
726
|
+
prop: 'text-align',
|
|
727
|
+
value: 'justify'
|
|
728
|
+
},
|
|
729
|
+
useQueryState: true,
|
|
730
|
+
contentDefault: '<b>J</b>',
|
|
731
|
+
contentFA: '<i class="fa fa-align-justify"></i>'
|
|
732
|
+
},
|
|
733
|
+
'justifyLeft': {
|
|
734
|
+
name: 'justifyLeft',
|
|
735
|
+
action: 'justifyLeft',
|
|
736
|
+
aria: 'left justify',
|
|
737
|
+
tagNames: [],
|
|
738
|
+
style: {
|
|
739
|
+
prop: 'text-align',
|
|
740
|
+
value: 'left'
|
|
741
|
+
},
|
|
742
|
+
useQueryState: true,
|
|
743
|
+
contentDefault: '<b>L</b>',
|
|
744
|
+
contentFA: '<i class="fa fa-align-left"></i>'
|
|
745
|
+
},
|
|
746
|
+
'justifyRight': {
|
|
747
|
+
name: 'justifyRight',
|
|
748
|
+
action: 'justifyRight',
|
|
749
|
+
aria: 'right justify',
|
|
750
|
+
tagNames: [],
|
|
751
|
+
style: {
|
|
752
|
+
prop: 'text-align',
|
|
753
|
+
value: 'right'
|
|
754
|
+
},
|
|
755
|
+
useQueryState: true,
|
|
756
|
+
contentDefault: '<b>R</b>',
|
|
757
|
+
contentFA: '<i class="fa fa-align-right"></i>'
|
|
758
|
+
},
|
|
759
|
+
'header1': {
|
|
760
|
+
name: 'header1',
|
|
761
|
+
action: function (options) {
|
|
762
|
+
return 'append-' + options.firstHeader;
|
|
763
|
+
},
|
|
764
|
+
aria: function (options) {
|
|
765
|
+
return options.firstHeader;
|
|
766
|
+
},
|
|
767
|
+
tagNames: function (options) {
|
|
768
|
+
return [options.firstHeader];
|
|
769
|
+
},
|
|
770
|
+
contentDefault: '<b>H1</b>'
|
|
771
|
+
},
|
|
772
|
+
'header2': {
|
|
773
|
+
name: 'header2',
|
|
774
|
+
action: function (options) {
|
|
775
|
+
return 'append-' + options.secondHeader;
|
|
776
|
+
},
|
|
777
|
+
aria: function (options) {
|
|
778
|
+
return options.secondHeader;
|
|
779
|
+
},
|
|
780
|
+
tagNames: function (options) {
|
|
781
|
+
return [options.secondHeader];
|
|
782
|
+
},
|
|
783
|
+
contentDefault: '<b>H2</b>'
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
DefaultButton = function (options, instance) {
|
|
788
|
+
this.options = options;
|
|
789
|
+
this.name = options.name;
|
|
790
|
+
this.init(instance);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
DefaultButton.prototype = {
|
|
794
|
+
init: function (instance) {
|
|
795
|
+
this.base = instance;
|
|
796
|
+
|
|
797
|
+
this.button = this.createButton();
|
|
798
|
+
this.base.on(this.button, 'click', this.handleClick.bind(this));
|
|
799
|
+
},
|
|
800
|
+
getButton: function () {
|
|
801
|
+
return this.button;
|
|
802
|
+
},
|
|
803
|
+
getAction: function () {
|
|
804
|
+
return (typeof this.options.action === 'function') ? this.options.action(this.base.options) : this.options.action;
|
|
805
|
+
},
|
|
806
|
+
getAria: function () {
|
|
807
|
+
return (typeof this.options.aria === 'function') ? this.options.aria(this.base.options) : this.options.aria;
|
|
808
|
+
},
|
|
809
|
+
getTagNames: function () {
|
|
810
|
+
return (typeof this.options.tagNames === 'function') ? this.options.tagNames(this.base.options) : this.options.tagNames;
|
|
811
|
+
},
|
|
812
|
+
createButton: function () {
|
|
813
|
+
var button = this.base.options.ownerDocument.createElement('button'),
|
|
814
|
+
content = this.options.contentDefault;
|
|
815
|
+
button.classList.add('medium-editor-action');
|
|
816
|
+
button.classList.add('medium-editor-action-' + this.name);
|
|
817
|
+
button.setAttribute('data-action', this.getAction());
|
|
818
|
+
button.setAttribute('aria-label', this.getAria());
|
|
819
|
+
if (this.base.options.buttonLabels) {
|
|
820
|
+
if (this.base.options.buttonLabels === 'fontawesome' && this.options.contentFA) {
|
|
821
|
+
content = this.options.contentFA;
|
|
822
|
+
} else if (typeof this.base.options.buttonLabels === 'object' && this.base.options.buttonLabels[this.name]) {
|
|
823
|
+
content = this.base.options.buttonLabels[this.options.name];
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
button.innerHTML = content;
|
|
827
|
+
return button;
|
|
828
|
+
},
|
|
829
|
+
handleClick: function (evt) {
|
|
830
|
+
evt.preventDefault();
|
|
831
|
+
evt.stopPropagation();
|
|
832
|
+
|
|
833
|
+
var action = this.getAction();
|
|
834
|
+
|
|
835
|
+
if (action) {
|
|
836
|
+
this.base.execAction(action);
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
isActive: function () {
|
|
840
|
+
return this.button.classList.contains(this.base.options.activeButtonClass);
|
|
841
|
+
},
|
|
842
|
+
setInactive: function () {
|
|
843
|
+
this.button.classList.remove(this.base.options.activeButtonClass);
|
|
844
|
+
delete this.knownState;
|
|
845
|
+
},
|
|
846
|
+
setActive: function () {
|
|
847
|
+
this.button.classList.add(this.base.options.activeButtonClass);
|
|
848
|
+
delete this.knownState;
|
|
849
|
+
},
|
|
850
|
+
queryCommandState: function () {
|
|
851
|
+
var queryState = null;
|
|
852
|
+
if (this.options.useQueryState) {
|
|
853
|
+
queryState = this.base.queryCommandState(this.getAction());
|
|
854
|
+
}
|
|
855
|
+
return queryState;
|
|
856
|
+
},
|
|
857
|
+
isAlreadyApplied: function (node) {
|
|
858
|
+
var isMatch = false,
|
|
859
|
+
tagNames = this.getTagNames(),
|
|
860
|
+
styleVals,
|
|
861
|
+
computedStyle;
|
|
862
|
+
|
|
863
|
+
if (this.knownState === false || this.knownState === true) {
|
|
864
|
+
return this.knownState;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
if (tagNames && tagNames.length > 0 && node.tagName) {
|
|
868
|
+
isMatch = tagNames.indexOf(node.tagName.toLowerCase()) !== -1;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (!isMatch && this.options.style) {
|
|
872
|
+
styleVals = this.options.style.value.split('|');
|
|
873
|
+
computedStyle = this.base.options.contentWindow.getComputedStyle(node, null).getPropertyValue(this.options.style.prop);
|
|
874
|
+
styleVals.forEach(function (val) {
|
|
875
|
+
if (!this.knownState) {
|
|
876
|
+
this.knownState = isMatch = (computedStyle.indexOf(val) !== -1);
|
|
877
|
+
}
|
|
878
|
+
}.bind(this));
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return isMatch;
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}(window, document));
|
|
885
|
+
|
|
886
|
+
var pasteHandler;
|
|
887
|
+
|
|
888
|
+
(function (window, document) {
|
|
889
|
+
'use strict';
|
|
890
|
+
/*jslint regexp: true*/
|
|
891
|
+
/*
|
|
892
|
+
jslint does not allow character negation, because the negation
|
|
893
|
+
will not match any unicode characters. In the regexes in this
|
|
894
|
+
block, negation is used specifically to match the end of an html
|
|
895
|
+
tag, and in fact unicode characters *should* be allowed.
|
|
896
|
+
*/
|
|
897
|
+
function createReplacements() {
|
|
898
|
+
return [
|
|
899
|
+
|
|
900
|
+
// replace two bogus tags that begin pastes from google docs
|
|
901
|
+
[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ""],
|
|
902
|
+
[new RegExp(/<\/b>(<br[^>]*>)?$/gi), ""],
|
|
903
|
+
|
|
904
|
+
// un-html spaces and newlines inserted by OS X
|
|
905
|
+
[new RegExp(/<span class="Apple-converted-space">\s+<\/span>/g), ' '],
|
|
906
|
+
[new RegExp(/<br class="Apple-interchange-newline">/g), '<br>'],
|
|
907
|
+
|
|
908
|
+
// replace google docs italics+bold with a span to be replaced once the html is inserted
|
|
909
|
+
[new RegExp(/<span[^>]*(font-style:italic;font-weight:bold|font-weight:bold;font-style:italic)[^>]*>/gi), '<span class="replace-with italic bold">'],
|
|
910
|
+
|
|
911
|
+
// replace google docs italics with a span to be replaced once the html is inserted
|
|
912
|
+
[new RegExp(/<span[^>]*font-style:italic[^>]*>/gi), '<span class="replace-with italic">'],
|
|
913
|
+
|
|
914
|
+
//[replace google docs bolds with a span to be replaced once the html is inserted
|
|
915
|
+
[new RegExp(/<span[^>]*font-weight:bold[^>]*>/gi), '<span class="replace-with bold">'],
|
|
916
|
+
|
|
917
|
+
// replace manually entered b/i/a tags with real ones
|
|
918
|
+
[new RegExp(/<(\/?)(i|b|a)>/gi), '<$1$2>'],
|
|
919
|
+
|
|
920
|
+
// replace manually a tags with real ones, converting smart-quotes from google docs
|
|
921
|
+
[new RegExp(/<a\s+href=("|”|“|“|”)([^&]+)("|”|“|“|”)>/gi), '<a href="$2">']
|
|
922
|
+
|
|
923
|
+
];
|
|
924
|
+
}
|
|
925
|
+
/*jslint regexp: false*/
|
|
926
|
+
|
|
927
|
+
pasteHandler = {
|
|
928
|
+
handlePaste: function (element, evt, options) {
|
|
929
|
+
var paragraphs,
|
|
930
|
+
html = '',
|
|
931
|
+
p,
|
|
932
|
+
dataFormatHTML = 'text/html',
|
|
933
|
+
dataFormatPlain = 'text/plain';
|
|
934
|
+
|
|
935
|
+
element.classList.remove('medium-editor-placeholder');
|
|
936
|
+
if (!options.forcePlainText && !options.cleanPastedHTML) {
|
|
937
|
+
return element;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (options.contentWindow.clipboardData && evt.clipboardData === undefined) {
|
|
941
|
+
evt.clipboardData = options.contentWindow.clipboardData;
|
|
942
|
+
// If window.clipboardData exists, but e.clipboardData doesn't exist,
|
|
943
|
+
// we're probably in IE. IE only has two possibilities for clipboard
|
|
944
|
+
// data format: 'Text' and 'URL'.
|
|
945
|
+
//
|
|
946
|
+
// Of the two, we want 'Text':
|
|
947
|
+
dataFormatHTML = 'Text';
|
|
948
|
+
dataFormatPlain = 'Text';
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (evt.clipboardData && evt.clipboardData.getData && !evt.defaultPrevented) {
|
|
952
|
+
evt.preventDefault();
|
|
953
|
+
|
|
954
|
+
if (options.cleanPastedHTML && evt.clipboardData.getData(dataFormatHTML)) {
|
|
955
|
+
return this.cleanPaste(evt.clipboardData.getData(dataFormatHTML), options);
|
|
956
|
+
}
|
|
957
|
+
if (!(options.disableReturn || element.getAttribute('data-disable-return'))) {
|
|
958
|
+
paragraphs = evt.clipboardData.getData(dataFormatPlain).split(/[\r\n]/g);
|
|
959
|
+
for (p = 0; p < paragraphs.length; p += 1) {
|
|
960
|
+
if (paragraphs[p] !== '') {
|
|
961
|
+
html += '<p>' + Util.htmlEntities(paragraphs[p]) + '</p>';
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
Util.insertHTMLCommand(options.ownerDocument, html);
|
|
965
|
+
} else {
|
|
966
|
+
html = Util.htmlEntities(evt.clipboardData.getData(dataFormatPlain));
|
|
967
|
+
Util.insertHTMLCommand(options.ownerDocument, html);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
|
|
972
|
+
cleanPaste: function (text, options) {
|
|
973
|
+
var i, elList, workEl,
|
|
974
|
+
el = Selection.getSelectionElement(options.contentWindow),
|
|
975
|
+
multiline = /<p|<br|<div/.test(text),
|
|
976
|
+
replacements = createReplacements();
|
|
977
|
+
|
|
978
|
+
for (i = 0; i < replacements.length; i += 1) {
|
|
979
|
+
text = text.replace(replacements[i][0], replacements[i][1]);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
if (multiline) {
|
|
983
|
+
// double br's aren't converted to p tags, but we want paragraphs.
|
|
984
|
+
elList = text.split('<br><br>');
|
|
985
|
+
|
|
986
|
+
this.pasteHTML('<p>' + elList.join('</p><p>') + '</p>', options.ownerDocument);
|
|
987
|
+
|
|
988
|
+
try {
|
|
989
|
+
options.ownerDocument.execCommand('insertText', false, "\n");
|
|
990
|
+
} catch (ignore) { }
|
|
991
|
+
|
|
992
|
+
// block element cleanup
|
|
993
|
+
elList = el.querySelectorAll('a,p,div,br');
|
|
994
|
+
for (i = 0; i < elList.length; i += 1) {
|
|
995
|
+
workEl = elList[i];
|
|
996
|
+
|
|
997
|
+
switch (workEl.tagName.toLowerCase()) {
|
|
998
|
+
case 'a':
|
|
999
|
+
if (options.targetBlank) {
|
|
1000
|
+
Util.setTargetBlank(workEl);
|
|
1001
|
+
}
|
|
1002
|
+
break;
|
|
1003
|
+
case 'p':
|
|
1004
|
+
case 'div':
|
|
1005
|
+
this.filterCommonBlocks(workEl);
|
|
1006
|
+
break;
|
|
1007
|
+
case 'br':
|
|
1008
|
+
this.filterLineBreak(workEl);
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
} else {
|
|
1013
|
+
this.pasteHTML(text, options.ownerDocument);
|
|
1014
|
+
}
|
|
1015
|
+
},
|
|
1016
|
+
|
|
1017
|
+
pasteHTML: function (html, ownerDocument) {
|
|
1018
|
+
var elList, workEl, i, fragmentBody, pasteBlock = ownerDocument.createDocumentFragment();
|
|
1019
|
+
|
|
1020
|
+
pasteBlock.appendChild(ownerDocument.createElement('body'));
|
|
1021
|
+
|
|
1022
|
+
fragmentBody = pasteBlock.querySelector('body');
|
|
1023
|
+
fragmentBody.innerHTML = html;
|
|
1024
|
+
|
|
1025
|
+
this.cleanupSpans(fragmentBody, ownerDocument);
|
|
1026
|
+
|
|
1027
|
+
elList = fragmentBody.querySelectorAll('*');
|
|
1028
|
+
for (i = 0; i < elList.length; i += 1) {
|
|
1029
|
+
workEl = elList[i];
|
|
1030
|
+
|
|
1031
|
+
// delete ugly attributes
|
|
1032
|
+
workEl.removeAttribute('class');
|
|
1033
|
+
workEl.removeAttribute('style');
|
|
1034
|
+
workEl.removeAttribute('dir');
|
|
1035
|
+
|
|
1036
|
+
if (workEl.tagName.toLowerCase() === 'meta') {
|
|
1037
|
+
workEl.parentNode.removeChild(workEl);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
Util.insertHTMLCommand(ownerDocument, fragmentBody.innerHTML.replace(/ /g, ' '));
|
|
1041
|
+
},
|
|
1042
|
+
isCommonBlock: function (el) {
|
|
1043
|
+
return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div'));
|
|
1044
|
+
},
|
|
1045
|
+
filterCommonBlocks: function (el) {
|
|
1046
|
+
if (/^\s*$/.test(el.textContent)) {
|
|
1047
|
+
el.parentNode.removeChild(el);
|
|
1048
|
+
}
|
|
1049
|
+
},
|
|
1050
|
+
filterLineBreak: function (el) {
|
|
1051
|
+
if (this.isCommonBlock(el.previousElementSibling)) {
|
|
1052
|
+
// remove stray br's following common block elements
|
|
1053
|
+
this.removeWithParent(el);
|
|
1054
|
+
} else if (this.isCommonBlock(el.parentNode) && (el.parentNode.firstChild === el || el.parentNode.lastChild === el)) {
|
|
1055
|
+
// remove br's just inside open or close tags of a div/p
|
|
1056
|
+
this.removeWithParent(el);
|
|
1057
|
+
} else if (el.parentNode.childElementCount === 1 && el.parentNode.textContent === '') {
|
|
1058
|
+
// and br's that are the only child of elements other than div/p
|
|
1059
|
+
this.removeWithParent(el);
|
|
1060
|
+
}
|
|
1061
|
+
},
|
|
1062
|
+
|
|
1063
|
+
// remove an element, including its parent, if it is the only element within its parent
|
|
1064
|
+
removeWithParent: function (el) {
|
|
1065
|
+
if (el && el.parentNode) {
|
|
1066
|
+
if (el.parentNode.parentNode && el.parentNode.childElementCount === 1) {
|
|
1067
|
+
el.parentNode.parentNode.removeChild(el.parentNode);
|
|
1068
|
+
} else {
|
|
1069
|
+
el.parentNode.removeChild(el);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
15
1073
|
|
|
16
|
-
|
|
17
|
-
|
|
1074
|
+
cleanupSpans: function (container_el, ownerDocument) {
|
|
1075
|
+
var i,
|
|
1076
|
+
el,
|
|
1077
|
+
new_el,
|
|
1078
|
+
spans = container_el.querySelectorAll('.replace-with'),
|
|
1079
|
+
isCEF = function (el) {
|
|
1080
|
+
return (el && el.nodeName !== '#text' && el.getAttribute('contenteditable') === 'false');
|
|
1081
|
+
};
|
|
18
1082
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1083
|
+
for (i = 0; i < spans.length; i += 1) {
|
|
1084
|
+
el = spans[i];
|
|
1085
|
+
new_el = ownerDocument.createElement(el.classList.contains('bold') ? 'b' : 'i');
|
|
1086
|
+
|
|
1087
|
+
if (el.classList.contains('bold') && el.classList.contains('italic')) {
|
|
1088
|
+
// add an i tag as well if this has both italics and bold
|
|
1089
|
+
new_el.innerHTML = '<i>' + el.innerHTML + '</i>';
|
|
1090
|
+
} else {
|
|
1091
|
+
new_el.innerHTML = el.innerHTML;
|
|
1092
|
+
}
|
|
1093
|
+
el.parentNode.replaceChild(new_el, el);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
spans = container_el.querySelectorAll('span');
|
|
1097
|
+
for (i = 0; i < spans.length; i += 1) {
|
|
1098
|
+
el = spans[i];
|
|
1099
|
+
|
|
1100
|
+
// bail if span is in contenteditable = false
|
|
1101
|
+
if (Util.traverseUp(el, isCEF)) {
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// remove empty spans, replace others with their contents
|
|
1106
|
+
if (/^\s*$/.test()) {
|
|
1107
|
+
el.parentNode.removeChild(el);
|
|
1108
|
+
} else {
|
|
1109
|
+
el.parentNode.replaceChild(ownerDocument.createTextNode(el.textContent), el);
|
|
1110
|
+
}
|
|
27
1111
|
}
|
|
28
1112
|
}
|
|
29
|
-
|
|
1113
|
+
};
|
|
1114
|
+
}(window, document));
|
|
1115
|
+
|
|
1116
|
+
var AnchorExtension;
|
|
1117
|
+
|
|
1118
|
+
(function (window, document) {
|
|
1119
|
+
'use strict';
|
|
1120
|
+
|
|
1121
|
+
function AnchorDerived() {
|
|
1122
|
+
this.parent = true;
|
|
1123
|
+
this.options = {
|
|
1124
|
+
name: 'anchor',
|
|
1125
|
+
action: 'createLink',
|
|
1126
|
+
aria: 'link',
|
|
1127
|
+
tagNames: ['a'],
|
|
1128
|
+
contentDefault: '<b>#</b>',
|
|
1129
|
+
contentFA: '<i class="fa fa-link"></i>'
|
|
1130
|
+
};
|
|
1131
|
+
this.name = 'anchor';
|
|
1132
|
+
this.hasForm = true;
|
|
30
1133
|
}
|
|
31
1134
|
|
|
32
|
-
|
|
33
|
-
var now = Date.now || function () {
|
|
34
|
-
return new Date().getTime();
|
|
35
|
-
};
|
|
1135
|
+
AnchorDerived.prototype = {
|
|
36
1136
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
previous = 0,
|
|
45
|
-
later;
|
|
46
|
-
|
|
47
|
-
if (!wait && wait !== 0) {
|
|
48
|
-
wait = THROTTLE_INTERVAL;
|
|
49
|
-
}
|
|
1137
|
+
// Button and Extension handling
|
|
1138
|
+
|
|
1139
|
+
// Called when the button the toolbar is clicked
|
|
1140
|
+
// Overrides DefaultButton.handleClick
|
|
1141
|
+
handleClick: function (evt) {
|
|
1142
|
+
evt.preventDefault();
|
|
1143
|
+
evt.stopPropagation();
|
|
50
1144
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
timeout = null;
|
|
54
|
-
result = func.apply(context, args);
|
|
55
|
-
if (!timeout) {
|
|
56
|
-
context = args = null;
|
|
1145
|
+
if (!this.base.selection) {
|
|
1146
|
+
this.base.checkSelection();
|
|
57
1147
|
}
|
|
58
|
-
};
|
|
59
1148
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
args = arguments;
|
|
65
|
-
if (remaining <= 0 || remaining > wait) {
|
|
66
|
-
clearTimeout(timeout);
|
|
67
|
-
timeout = null;
|
|
68
|
-
previous = currNow;
|
|
69
|
-
result = func.apply(context, args);
|
|
70
|
-
if (!timeout) {
|
|
71
|
-
context = args = null;
|
|
72
|
-
}
|
|
73
|
-
} else if (!timeout) {
|
|
74
|
-
timeout = setTimeout(later, remaining);
|
|
1149
|
+
var selectedParentElement = Selection.getSelectedParentElement(this.base.selectionRange);
|
|
1150
|
+
if (selectedParentElement.tagName &&
|
|
1151
|
+
selectedParentElement.tagName.toLowerCase() === 'a') {
|
|
1152
|
+
return this.base.execAction('unlink');
|
|
75
1153
|
}
|
|
76
|
-
return result;
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
1154
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
while (node !== null) {
|
|
83
|
-
if (node === parent) {
|
|
84
|
-
return true;
|
|
1155
|
+
if (!this.isDisplayed()) {
|
|
1156
|
+
this.showForm();
|
|
85
1157
|
}
|
|
86
|
-
node = node.parentNode;
|
|
87
|
-
}
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
1158
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
function findAdjacentTextNodeWithContent(rootNode, targetNode, ownerDocument) {
|
|
99
|
-
var pastTarget = false,
|
|
100
|
-
nextNode,
|
|
101
|
-
nodeIterator = ownerDocument.createNodeIterator(rootNode, NodeFilter.SHOW_TEXT, null, false);
|
|
102
|
-
|
|
103
|
-
// Use a native NodeIterator to iterate over all the text nodes that are descendants
|
|
104
|
-
// of the rootNode. Once past the targetNode, choose the first non-empty text node
|
|
105
|
-
nextNode = nodeIterator.nextNode();
|
|
106
|
-
while (nextNode) {
|
|
107
|
-
if (nextNode === targetNode) {
|
|
108
|
-
pastTarget = true;
|
|
109
|
-
} else if (pastTarget) {
|
|
110
|
-
if (nextNode.nodeType === 3 && nextNode.nodeValue && nextNode.nodeValue.trim().length > 0) {
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
1159
|
+
return false;
|
|
1160
|
+
},
|
|
1161
|
+
|
|
1162
|
+
// Called by medium-editor to append form to the toolbar
|
|
1163
|
+
getForm: function () {
|
|
1164
|
+
if (!this.anchorForm) {
|
|
1165
|
+
this.anchorForm = this.createForm();
|
|
113
1166
|
}
|
|
114
|
-
|
|
115
|
-
}
|
|
1167
|
+
return this.anchorForm;
|
|
1168
|
+
},
|
|
116
1169
|
|
|
117
|
-
|
|
118
|
-
|
|
1170
|
+
// Used by medium-editor when the default toolbar is to be displayed
|
|
1171
|
+
isDisplayed: function () {
|
|
1172
|
+
return this.getForm().style.display === 'block';
|
|
1173
|
+
},
|
|
119
1174
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
1175
|
+
hideForm: function () {
|
|
1176
|
+
this.getForm().style.display = 'none';
|
|
1177
|
+
this.getInput().value = '';
|
|
1178
|
+
},
|
|
1179
|
+
|
|
1180
|
+
showForm: function (link_value) {
|
|
1181
|
+
var input = this.getInput();
|
|
1182
|
+
|
|
1183
|
+
this.base.saveSelection();
|
|
1184
|
+
this.base.hideToolbarDefaultActions();
|
|
1185
|
+
this.getForm().style.display = 'block';
|
|
1186
|
+
this.base.setToolbarPosition();
|
|
1187
|
+
this.base.keepToolbarAlive = true;
|
|
1188
|
+
|
|
1189
|
+
input.value = link_value || '';
|
|
1190
|
+
input.focus();
|
|
1191
|
+
},
|
|
1192
|
+
|
|
1193
|
+
// Called by core when tearing down medium-editor (deactivate)
|
|
1194
|
+
deactivate: function () {
|
|
1195
|
+
if (!this.anchorForm) {
|
|
1196
|
+
return false;
|
|
131
1197
|
}
|
|
132
|
-
return ranges;
|
|
133
|
-
}
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
1198
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
len,
|
|
140
|
-
sel = this.options.contentWindow.getSelection();
|
|
141
|
-
if (savedSel) {
|
|
142
|
-
sel.removeAllRanges();
|
|
143
|
-
for (i = 0, len = savedSel.length; i < len; i += 1) {
|
|
144
|
-
sel.addRange(savedSel[i]);
|
|
1199
|
+
if (this.anchorForm.parentNode) {
|
|
1200
|
+
this.anchorForm.parentNode.removeChild(this.anchorForm);
|
|
145
1201
|
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
1202
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
function getSelectionStart() {
|
|
152
|
-
var node = this.options.ownerDocument.getSelection().anchorNode,
|
|
153
|
-
startNode = (node && node.nodeType === 3 ? node.parentNode : node);
|
|
154
|
-
return startNode;
|
|
155
|
-
}
|
|
1203
|
+
delete this.anchorForm;
|
|
1204
|
+
},
|
|
156
1205
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
container.appendChild(sel.getRangeAt(i).cloneContents());
|
|
171
|
-
}
|
|
172
|
-
html = container.innerHTML;
|
|
1206
|
+
// core methods
|
|
1207
|
+
|
|
1208
|
+
doLinkCreation: function () {
|
|
1209
|
+
var targetCheckbox = this.getForm().querySelector('.medium-editor-toolbar-anchor-target'),
|
|
1210
|
+
buttonCheckbox = this.getForm().querySelector('.medium-editor-toolbar-anchor-button'),
|
|
1211
|
+
opts = {
|
|
1212
|
+
url: this.getInput().value
|
|
1213
|
+
};
|
|
1214
|
+
|
|
1215
|
+
this.base.restoreSelection();
|
|
1216
|
+
|
|
1217
|
+
if (this.base.options.checkLinkFormat) {
|
|
1218
|
+
opts.url = this.checkLinkFormat(opts.url);
|
|
173
1219
|
}
|
|
174
|
-
|
|
175
|
-
if (
|
|
176
|
-
|
|
1220
|
+
|
|
1221
|
+
if (targetCheckbox && targetCheckbox.checked) {
|
|
1222
|
+
opts.target = "_blank";
|
|
1223
|
+
} else {
|
|
1224
|
+
opts.target = "_self";
|
|
177
1225
|
}
|
|
178
|
-
}
|
|
179
|
-
return html;
|
|
180
|
-
}
|
|
181
1226
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
var preCaretRange, postCaretRange;
|
|
191
|
-
|
|
192
|
-
if (!range) {
|
|
193
|
-
range = window.getSelection().getRangeAt(0);
|
|
194
|
-
}
|
|
1227
|
+
if (buttonCheckbox && buttonCheckbox.checked) {
|
|
1228
|
+
opts.buttonClass = this.base.options.anchorButtonClass;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
this.base.createLink(opts);
|
|
1232
|
+
this.base.keepToolbarAlive = false;
|
|
1233
|
+
this.base.checkSelection();
|
|
1234
|
+
},
|
|
195
1235
|
|
|
196
|
-
|
|
197
|
-
|
|
1236
|
+
checkLinkFormat: function (value) {
|
|
1237
|
+
var re = /^(https?|ftps?|rtmpt?):\/\/|mailto:/;
|
|
1238
|
+
return (re.test(value) ? '' : 'http://') + value;
|
|
1239
|
+
},
|
|
198
1240
|
|
|
199
|
-
|
|
200
|
-
|
|
1241
|
+
doFormCancel: function () {
|
|
1242
|
+
this.base.restoreSelection();
|
|
1243
|
+
this.base.keepToolbarAlive = false;
|
|
1244
|
+
this.base.checkSelection();
|
|
1245
|
+
},
|
|
201
1246
|
|
|
202
|
-
|
|
203
|
-
postCaretRange.setStart(range.endContainer, range.endOffset);
|
|
1247
|
+
// form creation and event handling
|
|
204
1248
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
1249
|
+
createForm: function () {
|
|
1250
|
+
var doc = this.base.options.ownerDocument,
|
|
1251
|
+
form = doc.createElement('div'),
|
|
1252
|
+
input = doc.createElement('input'),
|
|
1253
|
+
close = doc.createElement('a'),
|
|
1254
|
+
save = doc.createElement('a'),
|
|
1255
|
+
target,
|
|
1256
|
+
target_label,
|
|
1257
|
+
button,
|
|
1258
|
+
button_label;
|
|
210
1259
|
|
|
1260
|
+
// Anchor Form (div)
|
|
1261
|
+
form.className = 'medium-editor-toolbar-form';
|
|
1262
|
+
form.id = 'medium-editor-toolbar-form-anchor-' + this.base.id;
|
|
211
1263
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return !!(obj && obj.nodeType === 1);
|
|
215
|
-
}
|
|
1264
|
+
// Handle clicks on the form itself
|
|
1265
|
+
this.base.on(form, 'click', this.handleFormClick.bind(this));
|
|
216
1266
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
1267
|
+
// Add url textbox
|
|
1268
|
+
input.setAttribute('type', 'text');
|
|
1269
|
+
input.className = 'medium-editor-toolbar-input';
|
|
1270
|
+
input.setAttribute('placeholder', this.base.options.anchorInputPlaceholder);
|
|
1271
|
+
form.appendChild(input);
|
|
220
1272
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return doc.execCommand('insertHTML', false, html);
|
|
224
|
-
} catch (ignore) {}
|
|
225
|
-
}
|
|
1273
|
+
// Handle typing in the textbox
|
|
1274
|
+
this.base.on(input, 'keyup', this.handleTextboxKeyup.bind(this));
|
|
226
1275
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
1276
|
+
// Handle clicks into the textbox
|
|
1277
|
+
this.base.on(input, 'click', this.handleFormClick.bind(this));
|
|
1278
|
+
|
|
1279
|
+
// Add save buton
|
|
1280
|
+
save.setAttribute('href', '#');
|
|
1281
|
+
save.className = 'medium-editor-toobar-save';
|
|
1282
|
+
save.innerHTML = this.base.options.buttonLabels === 'fontawesome' ?
|
|
1283
|
+
'<i class="fa fa-check"></i>' :
|
|
1284
|
+
'✓';
|
|
1285
|
+
form.appendChild(save);
|
|
1286
|
+
|
|
1287
|
+
// Handle save button clicks (capture)
|
|
1288
|
+
this.base.on(save, 'click', this.handleSaveClick.bind(this), true);
|
|
1289
|
+
|
|
1290
|
+
// Add close button
|
|
1291
|
+
close.setAttribute('href', '#');
|
|
1292
|
+
close.className = 'medium-editor-toobar-close';
|
|
1293
|
+
close.innerHTML = this.base.options.buttonLabels === 'fontawesome' ?
|
|
1294
|
+
'<i class="fa fa-times"></i>' :
|
|
1295
|
+
'×';
|
|
1296
|
+
form.appendChild(close);
|
|
1297
|
+
|
|
1298
|
+
// Handle close button clicks
|
|
1299
|
+
this.base.on(close, 'click', this.handleCloseClick.bind(this));
|
|
1300
|
+
|
|
1301
|
+
// (Optional) Add 'open in new window' checkbox
|
|
1302
|
+
if (this.base.options.anchorTarget) {
|
|
1303
|
+
target = doc.createElement('input');
|
|
1304
|
+
target.setAttribute('type', 'checkbox');
|
|
1305
|
+
target.className = 'medium-editor-toolbar-anchor-target';
|
|
1306
|
+
|
|
1307
|
+
target_label = doc.createElement('label');
|
|
1308
|
+
target_label.innerHTML = this.base.options.anchorInputCheckboxLabel;
|
|
1309
|
+
target_label.insertBefore(target, target_label.firstChild);
|
|
231
1310
|
|
|
232
|
-
|
|
233
|
-
el.innerHTML = html;
|
|
234
|
-
fragment = doc.createDocumentFragment();
|
|
235
|
-
while (el.firstChild) {
|
|
236
|
-
node = el.firstChild;
|
|
237
|
-
lastNode = fragment.appendChild(node);
|
|
1311
|
+
form.appendChild(target_label);
|
|
238
1312
|
}
|
|
239
|
-
range.insertNode(fragment);
|
|
240
1313
|
|
|
241
|
-
//
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
1314
|
+
// (Optional) Add 'add button class to anchor' checkbox
|
|
1315
|
+
if (this.base.options.anchorButton) {
|
|
1316
|
+
button = doc.createElement('input');
|
|
1317
|
+
button.setAttribute('type', 'checkbox');
|
|
1318
|
+
button.className = 'medium-editor-toolbar-anchor-button';
|
|
1319
|
+
|
|
1320
|
+
button_label = doc.createElement('label');
|
|
1321
|
+
button_label.innerHTML = "Button";
|
|
1322
|
+
button_label.insertBefore(button, button_label.firstChild);
|
|
1323
|
+
|
|
1324
|
+
form.appendChild(button_label);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// Handle click (capture) & focus (capture) outside of the form
|
|
1328
|
+
this.base.on(doc.body, 'click', this.handleOutsideInteraction.bind(this), true);
|
|
1329
|
+
this.base.on(doc.body, 'focus', this.handleOutsideInteraction.bind(this), true);
|
|
1330
|
+
|
|
1331
|
+
return form;
|
|
1332
|
+
},
|
|
1333
|
+
|
|
1334
|
+
getInput: function () {
|
|
1335
|
+
return this.getForm().querySelector('input.medium-editor-toolbar-input');
|
|
1336
|
+
},
|
|
1337
|
+
|
|
1338
|
+
handleOutsideInteraction: function (event) {
|
|
1339
|
+
if (event.target !== this.getForm() &&
|
|
1340
|
+
!Util.isDescendant(this.getForm(), event.target) &&
|
|
1341
|
+
!Util.isDescendant(this.base.toolbarActions, event.target)) {
|
|
1342
|
+
this.base.keepToolbarAlive = false;
|
|
1343
|
+
this.base.checkSelection();
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
|
|
1347
|
+
handleTextboxKeyup: function (event) {
|
|
1348
|
+
// For ENTER -> create the anchor
|
|
1349
|
+
if (event.keyCode === Util.keyCode.ENTER) {
|
|
1350
|
+
event.preventDefault();
|
|
1351
|
+
this.doLinkCreation();
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// For ESCAPE -> close the form
|
|
1356
|
+
if (event.keyCode === Util.keyCode.ESCAPE) {
|
|
1357
|
+
event.preventDefault();
|
|
1358
|
+
this.doFormCancel();
|
|
248
1359
|
}
|
|
1360
|
+
},
|
|
1361
|
+
|
|
1362
|
+
handleFormClick: function (event) {
|
|
1363
|
+
// make sure not to hide form when clicking inside the form
|
|
1364
|
+
event.stopPropagation();
|
|
1365
|
+
this.base.keepToolbarAlive = true;
|
|
1366
|
+
},
|
|
1367
|
+
|
|
1368
|
+
handleSaveClick: function (event) {
|
|
1369
|
+
// Clicking Save -> create the anchor
|
|
1370
|
+
event.preventDefault();
|
|
1371
|
+
this.doLinkCreation();
|
|
1372
|
+
},
|
|
1373
|
+
|
|
1374
|
+
handleCloseClick: function (event) {
|
|
1375
|
+
// Click Close -> close the form
|
|
1376
|
+
event.preventDefault();
|
|
1377
|
+
this.doFormCancel();
|
|
249
1378
|
}
|
|
250
|
-
}
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
AnchorExtension = Util.derives(DefaultButton, AnchorDerived);
|
|
1382
|
+
}(window, document));
|
|
1383
|
+
|
|
1384
|
+
function MediumEditor(elements, options) {
|
|
1385
|
+
'use strict';
|
|
1386
|
+
return this.init(elements, options);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
(function () {
|
|
1390
|
+
'use strict';
|
|
1391
|
+
|
|
1392
|
+
MediumEditor.statics = {
|
|
1393
|
+
ButtonsData: ButtonsData,
|
|
1394
|
+
DefaultButton: DefaultButton,
|
|
1395
|
+
AnchorExtension: AnchorExtension
|
|
1396
|
+
};
|
|
251
1397
|
|
|
252
1398
|
MediumEditor.prototype = {
|
|
253
1399
|
defaults: {
|
|
@@ -266,9 +1412,10 @@ if (typeof module === 'object') {
|
|
|
266
1412
|
disableDoubleReturn: false,
|
|
267
1413
|
disableToolbar: false,
|
|
268
1414
|
disableEditing: false,
|
|
269
|
-
disableAnchorForm: false,
|
|
270
1415
|
disablePlaceholders: false,
|
|
1416
|
+
toolbarAlign: 'center',
|
|
271
1417
|
elementsContainer: false,
|
|
1418
|
+
imageDragging: true,
|
|
272
1419
|
standardizeSelectionStart: false,
|
|
273
1420
|
contentWindow: window,
|
|
274
1421
|
ownerDocument: document,
|
|
@@ -286,19 +1433,15 @@ if (typeof module === 'object') {
|
|
|
286
1433
|
lastButtonClass: 'medium-editor-button-last'
|
|
287
1434
|
},
|
|
288
1435
|
|
|
289
|
-
// http://stackoverflow.com/questions/17907445/how-to-detect-ie11#comment30165888_17907562
|
|
290
|
-
// by rg89
|
|
291
|
-
isIE: ((navigator.appName === 'Microsoft Internet Explorer') || ((navigator.appName === 'Netscape') && (new RegExp('Trident/.*rv:([0-9]{1,}[.0-9]{0,})').exec(navigator.userAgent) !== null))),
|
|
292
|
-
|
|
293
1436
|
init: function (elements, options) {
|
|
294
1437
|
var uniqueId = 1;
|
|
295
1438
|
|
|
296
|
-
this.options =
|
|
1439
|
+
this.options = Util.defaults(options, this.defaults);
|
|
297
1440
|
this.setElementSelection(elements);
|
|
298
1441
|
if (this.elements.length === 0) {
|
|
299
1442
|
return;
|
|
300
1443
|
}
|
|
301
|
-
|
|
1444
|
+
|
|
302
1445
|
if (!this.options.elementsContainer) {
|
|
303
1446
|
this.options.elementsContainer = this.options.ownerDocument.body;
|
|
304
1447
|
}
|
|
@@ -316,13 +1459,14 @@ if (typeof module === 'object') {
|
|
|
316
1459
|
this.events = [];
|
|
317
1460
|
this.isActive = true;
|
|
318
1461
|
this.initThrottledMethods()
|
|
1462
|
+
.initCommands()
|
|
319
1463
|
.initElements()
|
|
320
1464
|
.bindSelect()
|
|
1465
|
+
.bindDragDrop()
|
|
321
1466
|
.bindPaste()
|
|
322
1467
|
.setPlaceholders()
|
|
323
1468
|
.bindElementActions()
|
|
324
1469
|
.bindWindowActions();
|
|
325
|
-
//.passInstance();
|
|
326
1470
|
},
|
|
327
1471
|
|
|
328
1472
|
on: function (target, event, listener, useCapture) {
|
|
@@ -373,7 +1517,7 @@ if (typeof module === 'object') {
|
|
|
373
1517
|
// handleResize is throttled because:
|
|
374
1518
|
// - It will be called when the browser is resizing, which can fire many times very quickly
|
|
375
1519
|
// - For some event (like resize) a slight lag in UI responsiveness is OK and provides performance benefits
|
|
376
|
-
this.handleResize = throttle(function () {
|
|
1520
|
+
this.handleResize = Util.throttle(function () {
|
|
377
1521
|
if (self.isActive) {
|
|
378
1522
|
self.positionToolbarIfShown();
|
|
379
1523
|
}
|
|
@@ -383,7 +1527,7 @@ if (typeof module === 'object') {
|
|
|
383
1527
|
// - This method could be called many times due to the type of event handlers that are calling it
|
|
384
1528
|
// - We want a slight delay so that other events in the stack can run, some of which may
|
|
385
1529
|
// prevent the toolbar from being hidden (via this.keepToolbarAlive).
|
|
386
|
-
this.handleBlur = throttle(function () {
|
|
1530
|
+
this.handleBlur = Util.throttle(function () {
|
|
387
1531
|
if (self.isActive && !self.keepToolbarAlive) {
|
|
388
1532
|
self.hideToolbarActions();
|
|
389
1533
|
}
|
|
@@ -412,11 +1556,8 @@ if (typeof module === 'object') {
|
|
|
412
1556
|
}
|
|
413
1557
|
// Init toolbar
|
|
414
1558
|
if (addToolbar) {
|
|
415
|
-
this.
|
|
416
|
-
.
|
|
417
|
-
.initToolbar()
|
|
418
|
-
.bindButtons()
|
|
419
|
-
.bindAnchorForm()
|
|
1559
|
+
this.initToolbar()
|
|
1560
|
+
.setFirstAndLastButtons()
|
|
420
1561
|
.bindAnchorPreview();
|
|
421
1562
|
}
|
|
422
1563
|
return this;
|
|
@@ -431,7 +1572,7 @@ if (typeof module === 'object') {
|
|
|
431
1572
|
selector = this.options.ownerDocument.querySelectorAll(selector);
|
|
432
1573
|
}
|
|
433
1574
|
// If element, put into array
|
|
434
|
-
if (isElement(selector)) {
|
|
1575
|
+
if (Util.isElement(selector)) {
|
|
435
1576
|
selector = [selector];
|
|
436
1577
|
}
|
|
437
1578
|
// Convert NodeList (or other array like object) into an array
|
|
@@ -442,9 +1583,18 @@ if (typeof module === 'object') {
|
|
|
442
1583
|
var self = this,
|
|
443
1584
|
blurFunction = function (e) {
|
|
444
1585
|
var isDescendantOfEditorElements = false,
|
|
1586
|
+
selection = self.options.contentWindow.getSelection(),
|
|
1587
|
+
selRange = selection.isCollapsed ?
|
|
1588
|
+
null :
|
|
1589
|
+
Selection.getSelectedParentElement(selection.getRangeAt(0)),
|
|
445
1590
|
i;
|
|
1591
|
+
|
|
1592
|
+
// This control was introduced also to avoid the toolbar
|
|
1593
|
+
// to disapper when selecting from right to left and
|
|
1594
|
+
// the selection ends at the beginning of the text.
|
|
446
1595
|
for (i = 0; i < self.elements.length; i += 1) {
|
|
447
|
-
if (isDescendant(self.elements[i], e.target)
|
|
1596
|
+
if (Util.isDescendant(self.elements[i], e.target)
|
|
1597
|
+
|| Util.isDescendant(self.elements[i], selRange)) {
|
|
448
1598
|
isDescendantOfEditorElements = true;
|
|
449
1599
|
break;
|
|
450
1600
|
}
|
|
@@ -453,8 +1603,8 @@ if (typeof module === 'object') {
|
|
|
453
1603
|
if (e.target !== self.toolbar
|
|
454
1604
|
&& self.elements.indexOf(e.target) === -1
|
|
455
1605
|
&& !isDescendantOfEditorElements
|
|
456
|
-
&& !isDescendant(self.toolbar, e.target)
|
|
457
|
-
&& !isDescendant(self.anchorPreview, e.target)) {
|
|
1606
|
+
&& !Util.isDescendant(self.toolbar, e.target)
|
|
1607
|
+
&& !Util.isDescendant(self.anchorPreview, e.target)) {
|
|
458
1608
|
|
|
459
1609
|
// Activate the placeholder
|
|
460
1610
|
if (!self.options.disablePlaceholders) {
|
|
@@ -507,7 +1657,6 @@ if (typeof module === 'object') {
|
|
|
507
1657
|
// Bind the return and tab keypress events
|
|
508
1658
|
this.bindReturn(i)
|
|
509
1659
|
.bindKeydown(i)
|
|
510
|
-
.bindBlur()
|
|
511
1660
|
.bindClick(i);
|
|
512
1661
|
}
|
|
513
1662
|
|
|
@@ -544,6 +1693,60 @@ if (typeof module === 'object') {
|
|
|
544
1693
|
return content;
|
|
545
1694
|
},
|
|
546
1695
|
|
|
1696
|
+
initExtension: function (extension, name) {
|
|
1697
|
+
if (extension.parent) {
|
|
1698
|
+
extension.base = this;
|
|
1699
|
+
}
|
|
1700
|
+
if (typeof extension.init === 'function') {
|
|
1701
|
+
extension.init(this);
|
|
1702
|
+
}
|
|
1703
|
+
if (!extension.name) {
|
|
1704
|
+
extension.name = name;
|
|
1705
|
+
}
|
|
1706
|
+
return extension;
|
|
1707
|
+
},
|
|
1708
|
+
|
|
1709
|
+
initCommands: function () {
|
|
1710
|
+
var buttons = this.options.buttons,
|
|
1711
|
+
extensions = this.options.extensions,
|
|
1712
|
+
ext,
|
|
1713
|
+
name;
|
|
1714
|
+
this.commands = [];
|
|
1715
|
+
|
|
1716
|
+
buttons.forEach(function (buttonName) {
|
|
1717
|
+
if (extensions[buttonName]) {
|
|
1718
|
+
ext = this.initExtension(extensions[buttonName], buttonName);
|
|
1719
|
+
this.commands.push(ext);
|
|
1720
|
+
} else if (buttonName === 'anchor') {
|
|
1721
|
+
ext = this.initExtension(new AnchorExtension(), buttonName);
|
|
1722
|
+
this.commands.push(ext);
|
|
1723
|
+
} else if (ButtonsData.hasOwnProperty(buttonName)) {
|
|
1724
|
+
ext = new DefaultButton(ButtonsData[buttonName], this);
|
|
1725
|
+
this.commands.push(ext);
|
|
1726
|
+
}
|
|
1727
|
+
}.bind(this));
|
|
1728
|
+
|
|
1729
|
+
for (name in extensions) {
|
|
1730
|
+
if (extensions.hasOwnProperty(name) && buttons.indexOf(name) === -1) {
|
|
1731
|
+
ext = this.initExtension(extensions[name], name);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
return this;
|
|
1736
|
+
},
|
|
1737
|
+
|
|
1738
|
+
getExtensionByName: function (name) {
|
|
1739
|
+
var extension;
|
|
1740
|
+
if (this.commands && this.commands.length) {
|
|
1741
|
+
this.commands.forEach(function (ext) {
|
|
1742
|
+
if (ext.name === name) {
|
|
1743
|
+
extension = ext;
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
return extension;
|
|
1748
|
+
},
|
|
1749
|
+
|
|
547
1750
|
/**
|
|
548
1751
|
* Helper function to call a method with a number of parameters on all registered extensions.
|
|
549
1752
|
* The function assures that the function exists before calling.
|
|
@@ -571,36 +1774,13 @@ if (typeof module === 'object') {
|
|
|
571
1774
|
return this;
|
|
572
1775
|
},
|
|
573
1776
|
|
|
574
|
-
/**
|
|
575
|
-
* Pass current Medium Editor instance to all extensions
|
|
576
|
-
* if extension constructor has 'parent' attribute set to 'true'
|
|
577
|
-
*
|
|
578
|
-
*/
|
|
579
|
-
passInstance: function () {
|
|
580
|
-
var self = this,
|
|
581
|
-
ext,
|
|
582
|
-
name;
|
|
583
|
-
|
|
584
|
-
for (name in self.options.extensions) {
|
|
585
|
-
if (self.options.extensions.hasOwnProperty(name)) {
|
|
586
|
-
ext = self.options.extensions[name];
|
|
587
|
-
|
|
588
|
-
if (ext.parent) {
|
|
589
|
-
ext.base = self;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return self;
|
|
595
|
-
},
|
|
596
|
-
|
|
597
1777
|
bindParagraphCreation: function (index) {
|
|
598
1778
|
var self = this;
|
|
599
1779
|
this.on(this.elements[index], 'keypress', function (e) {
|
|
600
1780
|
var node,
|
|
601
1781
|
tagName;
|
|
602
|
-
if (e.which ===
|
|
603
|
-
node = getSelectionStart
|
|
1782
|
+
if (e.which === Util.keyCode.SPACE) {
|
|
1783
|
+
node = Selection.getSelectionStart(self.options.ownerDocument);
|
|
604
1784
|
tagName = node.tagName.toLowerCase();
|
|
605
1785
|
if (tagName === 'a') {
|
|
606
1786
|
self.options.ownerDocument.execCommand('unlink', false, null);
|
|
@@ -609,22 +1789,22 @@ if (typeof module === 'object') {
|
|
|
609
1789
|
});
|
|
610
1790
|
|
|
611
1791
|
this.on(this.elements[index], 'keyup', function (e) {
|
|
612
|
-
var node = getSelectionStart
|
|
1792
|
+
var node = Selection.getSelectionStart(self.options.ownerDocument),
|
|
613
1793
|
tagName,
|
|
614
1794
|
editorElement;
|
|
615
1795
|
|
|
616
1796
|
if (node && node.getAttribute('data-medium-element') && node.children.length === 0 && !(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
|
|
617
1797
|
self.options.ownerDocument.execCommand('formatBlock', false, 'p');
|
|
618
1798
|
}
|
|
619
|
-
if (e.which ===
|
|
620
|
-
node = getSelectionStart
|
|
1799
|
+
if (e.which === Util.keyCode.ENTER) {
|
|
1800
|
+
node = Selection.getSelectionStart(self.options.ownerDocument);
|
|
621
1801
|
tagName = node.tagName.toLowerCase();
|
|
622
|
-
editorElement =
|
|
1802
|
+
editorElement = Selection.getSelectionElement(self.options.contentWindow);
|
|
623
1803
|
|
|
624
1804
|
if (!(self.options.disableReturn || editorElement.getAttribute('data-disable-return')) &&
|
|
625
|
-
tagName !== 'li' && !
|
|
626
|
-
|
|
627
|
-
|
|
1805
|
+
tagName !== 'li' && !Util.isListItemChild(node)) {
|
|
1806
|
+
|
|
1807
|
+
if (!e.shiftKey && !e.ctrlKey) {
|
|
628
1808
|
// paragraph creation should not be forced within a header tag
|
|
629
1809
|
if (!/h\d/.test(tagName)) {
|
|
630
1810
|
self.options.ownerDocument.execCommand('formatBlock', false, 'p');
|
|
@@ -639,32 +1819,15 @@ if (typeof module === 'object') {
|
|
|
639
1819
|
return this;
|
|
640
1820
|
},
|
|
641
1821
|
|
|
642
|
-
isListItemChild: function (node) {
|
|
643
|
-
var parentNode = node.parentNode,
|
|
644
|
-
tagName = parentNode.tagName.toLowerCase();
|
|
645
|
-
while (this.parentElements.indexOf(tagName) === -1 && tagName !== 'div') {
|
|
646
|
-
if (tagName === 'li') {
|
|
647
|
-
return true;
|
|
648
|
-
}
|
|
649
|
-
parentNode = parentNode.parentNode;
|
|
650
|
-
if (parentNode && parentNode.tagName) {
|
|
651
|
-
tagName = parentNode.tagName.toLowerCase();
|
|
652
|
-
} else {
|
|
653
|
-
return false;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
return false;
|
|
657
|
-
},
|
|
658
|
-
|
|
659
1822
|
bindReturn: function (index) {
|
|
660
1823
|
var self = this;
|
|
661
1824
|
this.on(this.elements[index], 'keypress', function (e) {
|
|
662
|
-
if (e.which ===
|
|
1825
|
+
if (e.which === Util.keyCode.ENTER) {
|
|
663
1826
|
if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
|
|
664
1827
|
e.preventDefault();
|
|
665
1828
|
} else if (self.options.disableDoubleReturn || this.getAttribute('data-disable-double-return')) {
|
|
666
|
-
var node = getSelectionStart
|
|
667
|
-
if (node && node.textContent === '
|
|
1829
|
+
var node = Selection.getSelectionStart(self.options.contentWindow);
|
|
1830
|
+
if (node && node.textContent.trim() === '') {
|
|
668
1831
|
e.preventDefault();
|
|
669
1832
|
}
|
|
670
1833
|
}
|
|
@@ -676,17 +1839,20 @@ if (typeof module === 'object') {
|
|
|
676
1839
|
bindKeydown: function (index) {
|
|
677
1840
|
var self = this;
|
|
678
1841
|
this.on(this.elements[index], 'keydown', function (e) {
|
|
1842
|
+
var node, tag, key;
|
|
679
1843
|
|
|
680
|
-
if (e.which ===
|
|
1844
|
+
if (e.which === Util.keyCode.TAB) {
|
|
681
1845
|
// Override tab only for pre nodes
|
|
682
|
-
|
|
1846
|
+
node = Selection.getSelectionStart(self.options.ownerDocument);
|
|
1847
|
+
tag = node && node.tagName.toLowerCase();
|
|
1848
|
+
|
|
683
1849
|
if (tag === 'pre') {
|
|
684
1850
|
e.preventDefault();
|
|
685
1851
|
self.options.ownerDocument.execCommand('insertHtml', null, ' ');
|
|
686
1852
|
}
|
|
687
1853
|
|
|
688
1854
|
// Tab to indent list structures!
|
|
689
|
-
if (tag === 'li') {
|
|
1855
|
+
if (tag === 'li' || Util.isListItemChild(node)) {
|
|
690
1856
|
e.preventDefault();
|
|
691
1857
|
|
|
692
1858
|
// If Shift is down, outdent, otherwise indent
|
|
@@ -696,36 +1862,42 @@ if (typeof module === 'object') {
|
|
|
696
1862
|
self.options.ownerDocument.execCommand('indent', e);
|
|
697
1863
|
}
|
|
698
1864
|
}
|
|
699
|
-
} else if (e.which ===
|
|
1865
|
+
} else if (e.which === Util.keyCode.BACKSPACE || e.which === Util.keyCode.DELETE || e.which === Util.keyCode.ENTER) {
|
|
700
1866
|
|
|
701
1867
|
// Bind keys which can create or destroy a block element: backspace, delete, return
|
|
702
1868
|
self.onBlockModifier(e);
|
|
703
1869
|
|
|
1870
|
+
} else if (e.ctrlKey || e.metaKey) {
|
|
1871
|
+
key = String.fromCharCode(e.which || e.keyCode).toLowerCase();
|
|
1872
|
+
self.commands.forEach(function (extension) {
|
|
1873
|
+
if (extension.options.key && extension.options.key === key) {
|
|
1874
|
+
extension.handleClick(e);
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
704
1877
|
}
|
|
705
1878
|
});
|
|
706
1879
|
return this;
|
|
707
1880
|
},
|
|
708
1881
|
|
|
709
1882
|
onBlockModifier: function (e) {
|
|
710
|
-
var range, sel, p, node = getSelectionStart
|
|
1883
|
+
var range, sel, p, node = Selection.getSelectionStart(this.options.ownerDocument),
|
|
711
1884
|
tagName = node.tagName.toLowerCase(),
|
|
712
1885
|
isEmpty = /^(\s+|<br\/?>)?$/i,
|
|
713
1886
|
isHeader = /h\d/i;
|
|
714
1887
|
|
|
715
|
-
|
|
716
|
-
if ((e.which === 8 || e.which === 13)
|
|
1888
|
+
if ((e.which === Util.keyCode.BACKSPACE || e.which === Util.keyCode.ENTER)
|
|
717
1889
|
&& node.previousElementSibling
|
|
718
1890
|
// in a header
|
|
719
1891
|
&& isHeader.test(tagName)
|
|
720
1892
|
// at the very end of the block
|
|
721
|
-
&& getCaretOffsets(node).left === 0) {
|
|
722
|
-
if (e.which ===
|
|
1893
|
+
&& Selection.getCaretOffsets(node).left === 0) {
|
|
1894
|
+
if (e.which === Util.keyCode.BACKSPACE && isEmpty.test(node.previousElementSibling.innerHTML)) {
|
|
723
1895
|
// backspacing the begining of a header into an empty previous element will
|
|
724
1896
|
// change the tagName of the current node to prevent one
|
|
725
1897
|
// instead delete previous node and cancel the event.
|
|
726
1898
|
node.previousElementSibling.parentNode.removeChild(node.previousElementSibling);
|
|
727
1899
|
e.preventDefault();
|
|
728
|
-
} else if (e.which ===
|
|
1900
|
+
} else if (e.which === Util.keyCode.ENTER) {
|
|
729
1901
|
// hitting return in the begining of a header will create empty header elements before the current one
|
|
730
1902
|
// instead, make "<p><br></p>" element, which are what happens if you hit return in an empty paragraph
|
|
731
1903
|
p = this.options.ownerDocument.createElement('p');
|
|
@@ -733,9 +1905,7 @@ if (typeof module === 'object') {
|
|
|
733
1905
|
node.previousElementSibling.parentNode.insertBefore(p, node);
|
|
734
1906
|
e.preventDefault();
|
|
735
1907
|
}
|
|
736
|
-
|
|
737
|
-
// delete
|
|
738
|
-
} else if (e.which === 46
|
|
1908
|
+
} else if (e.which === Util.keyCode.DELETE
|
|
739
1909
|
&& node.nextElementSibling
|
|
740
1910
|
&& node.previousElementSibling
|
|
741
1911
|
// not in a header
|
|
@@ -757,100 +1927,12 @@ if (typeof module === 'object') {
|
|
|
757
1927
|
range.collapse(true);
|
|
758
1928
|
|
|
759
1929
|
sel.removeAllRanges();
|
|
760
|
-
sel.addRange(range);
|
|
761
|
-
|
|
762
|
-
node.previousElementSibling.parentNode.removeChild(node);
|
|
763
|
-
|
|
764
|
-
e.preventDefault();
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
},
|
|
1930
|
+
sel.addRange(range);
|
|
768
1931
|
|
|
769
|
-
|
|
770
|
-
var buttonLabels = this.getButtonLabels(this.options.buttonLabels),
|
|
771
|
-
buttonTemplates = {
|
|
772
|
-
'bold': '<button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b" aria-label="bold">' + buttonLabels.bold + '</button>',
|
|
773
|
-
'italic': '<button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i" aria-label="italic">' + buttonLabels.italic + '</button>',
|
|
774
|
-
'underline': '<button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u" aria-label="underline">' + buttonLabels.underline + '</button>',
|
|
775
|
-
'strikethrough': '<button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike" aria-label="strike through">' + buttonLabels.strikethrough + '</button>',
|
|
776
|
-
'superscript': '<button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup" aria-label="superscript">' + buttonLabels.superscript + '</button>',
|
|
777
|
-
'subscript': '<button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub" aria-label="subscript">' + buttonLabels.subscript + '</button>',
|
|
778
|
-
'anchor': '<button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a" aria-label="link">' + buttonLabels.anchor + '</button>',
|
|
779
|
-
'image': '<button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img" aria-label="image">' + buttonLabels.image + '</button>',
|
|
780
|
-
'header1': '<button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '" aria-label="h1">' + buttonLabels.header1 + '</button>',
|
|
781
|
-
'header2': '<button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + ' "aria-label="h2">' + buttonLabels.header2 + '</button>',
|
|
782
|
-
'quote': '<button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote" aria-label="blockquote">' + buttonLabels.quote + '</button>',
|
|
783
|
-
'orderedlist': '<button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol" aria-label="ordered list">' + buttonLabels.orderedlist + '</button>',
|
|
784
|
-
'unorderedlist': '<button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul" aria-label="unordered list">' + buttonLabels.unorderedlist + '</button>',
|
|
785
|
-
'pre': '<button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre" aria-label="preformatted text">' + buttonLabels.pre + '</button>',
|
|
786
|
-
'indent': '<button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul" aria-label="indent">' + buttonLabels.indent + '</button>',
|
|
787
|
-
'outdent': '<button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul" aria-label="outdent">' + buttonLabels.outdent + '</button>',
|
|
788
|
-
'justifyCenter': '<button class="medium-editor-action medium-editor-action-justifyCenter" data-action="justifyCenter" data-element="" aria-label="center justify">' + buttonLabels.justifyCenter + '</button>',
|
|
789
|
-
'justifyFull': '<button class="medium-editor-action medium-editor-action-justifyFull" data-action="justifyFull" data-element="" aria-label="full justify">' + buttonLabels.justifyFull + '</button>',
|
|
790
|
-
'justifyLeft': '<button class="medium-editor-action medium-editor-action-justifyLeft" data-action="justifyLeft" data-element="" aria-label="left justify">' + buttonLabels.justifyLeft + '</button>',
|
|
791
|
-
'justifyRight': '<button class="medium-editor-action medium-editor-action-justifyRight" data-action="justifyRight" data-element="" aria-label="right justify">' + buttonLabels.justifyRight + '</button>'
|
|
792
|
-
};
|
|
793
|
-
return buttonTemplates[btnType] || false;
|
|
794
|
-
},
|
|
1932
|
+
node.previousElementSibling.parentNode.removeChild(node);
|
|
795
1933
|
|
|
796
|
-
|
|
797
|
-
getButtonLabels: function (buttonLabelType) {
|
|
798
|
-
var customButtonLabels,
|
|
799
|
-
attrname,
|
|
800
|
-
buttonLabels = {
|
|
801
|
-
'bold': '<b>B</b>',
|
|
802
|
-
'italic': '<b><i>I</i></b>',
|
|
803
|
-
'underline': '<b><u>U</u></b>',
|
|
804
|
-
'strikethrough': '<s>A</s>',
|
|
805
|
-
'superscript': '<b>x<sup>1</sup></b>',
|
|
806
|
-
'subscript': '<b>x<sub>1</sub></b>',
|
|
807
|
-
'anchor': '<b>#</b>',
|
|
808
|
-
'image': '<b>image</b>',
|
|
809
|
-
'header1': '<b>H1</b>',
|
|
810
|
-
'header2': '<b>H2</b>',
|
|
811
|
-
'quote': '<b>“</b>',
|
|
812
|
-
'orderedlist': '<b>1.</b>',
|
|
813
|
-
'unorderedlist': '<b>•</b>',
|
|
814
|
-
'pre': '<b>0101</b>',
|
|
815
|
-
'indent': '<b>→</b>',
|
|
816
|
-
'outdent': '<b>←</b>',
|
|
817
|
-
'justifyCenter': '<b>C</b>',
|
|
818
|
-
'justifyFull': '<b>J</b>',
|
|
819
|
-
'justifyLeft': '<b>L</b>',
|
|
820
|
-
'justifyRight': '<b>R</b>'
|
|
821
|
-
};
|
|
822
|
-
if (buttonLabelType === 'fontawesome') {
|
|
823
|
-
customButtonLabels = {
|
|
824
|
-
'bold': '<i class="fa fa-bold"></i>',
|
|
825
|
-
'italic': '<i class="fa fa-italic"></i>',
|
|
826
|
-
'underline': '<i class="fa fa-underline"></i>',
|
|
827
|
-
'strikethrough': '<i class="fa fa-strikethrough"></i>',
|
|
828
|
-
'superscript': '<i class="fa fa-superscript"></i>',
|
|
829
|
-
'subscript': '<i class="fa fa-subscript"></i>',
|
|
830
|
-
'anchor': '<i class="fa fa-link"></i>',
|
|
831
|
-
'image': '<i class="fa fa-picture-o"></i>',
|
|
832
|
-
'quote': '<i class="fa fa-quote-right"></i>',
|
|
833
|
-
'orderedlist': '<i class="fa fa-list-ol"></i>',
|
|
834
|
-
'unorderedlist': '<i class="fa fa-list-ul"></i>',
|
|
835
|
-
'pre': '<i class="fa fa-code fa-lg"></i>',
|
|
836
|
-
'indent': '<i class="fa fa-indent"></i>',
|
|
837
|
-
'outdent': '<i class="fa fa-outdent"></i>',
|
|
838
|
-
'justifyCenter': '<i class="fa fa-align-center"></i>',
|
|
839
|
-
'justifyFull': '<i class="fa fa-align-justify"></i>',
|
|
840
|
-
'justifyLeft': '<i class="fa fa-align-left"></i>',
|
|
841
|
-
'justifyRight': '<i class="fa fa-align-right"></i>'
|
|
842
|
-
};
|
|
843
|
-
} else if (typeof buttonLabelType === 'object') {
|
|
844
|
-
customButtonLabels = buttonLabelType;
|
|
845
|
-
}
|
|
846
|
-
if (typeof customButtonLabels === 'object') {
|
|
847
|
-
for (attrname in customButtonLabels) {
|
|
848
|
-
if (customButtonLabels.hasOwnProperty(attrname)) {
|
|
849
|
-
buttonLabels[attrname] = customButtonLabels[attrname];
|
|
850
|
-
}
|
|
851
|
-
}
|
|
1934
|
+
e.preventDefault();
|
|
852
1935
|
}
|
|
853
|
-
return buttonLabels;
|
|
854
1936
|
},
|
|
855
1937
|
|
|
856
1938
|
initToolbar: function () {
|
|
@@ -858,17 +1940,10 @@ if (typeof module === 'object') {
|
|
|
858
1940
|
return this;
|
|
859
1941
|
}
|
|
860
1942
|
this.toolbar = this.createToolbar();
|
|
861
|
-
this.addExtensionForms();
|
|
862
1943
|
this.keepToolbarAlive = false;
|
|
863
1944
|
this.toolbarActions = this.toolbar.querySelector('.medium-editor-toolbar-actions');
|
|
864
1945
|
this.anchorPreview = this.createAnchorPreview();
|
|
865
1946
|
|
|
866
|
-
if (!this.options.disableAnchorForm) {
|
|
867
|
-
this.anchorForm = this.toolbar.querySelector('.medium-editor-toolbar-form');
|
|
868
|
-
this.anchorInput = this.anchorForm.querySelector('input.medium-editor-toolbar-input');
|
|
869
|
-
this.anchorTarget = this.anchorForm.querySelector('input.medium-editor-toolbar-anchor-target');
|
|
870
|
-
this.anchorButton = this.anchorForm.querySelector('input.medium-editor-toolbar-anchor-button');
|
|
871
|
-
}
|
|
872
1947
|
return this;
|
|
873
1948
|
},
|
|
874
1949
|
|
|
@@ -884,145 +1959,127 @@ if (typeof module === 'object') {
|
|
|
884
1959
|
}
|
|
885
1960
|
|
|
886
1961
|
toolbar.appendChild(this.toolbarButtons());
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1962
|
+
|
|
1963
|
+
// Add any forms that extensions may have
|
|
1964
|
+
this.commands.forEach(function (extension) {
|
|
1965
|
+
if (extension.hasForm) {
|
|
1966
|
+
toolbar.appendChild(extension.getForm());
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
|
|
890
1970
|
this.options.elementsContainer.appendChild(toolbar);
|
|
891
1971
|
return toolbar;
|
|
892
1972
|
},
|
|
893
1973
|
|
|
894
1974
|
//TODO: actionTemplate
|
|
895
1975
|
toolbarButtons: function () {
|
|
896
|
-
var
|
|
897
|
-
ul = this.options.ownerDocument.createElement('ul'),
|
|
1976
|
+
var ul = this.options.ownerDocument.createElement('ul'),
|
|
898
1977
|
li,
|
|
899
|
-
|
|
900
|
-
btn,
|
|
901
|
-
ext;
|
|
1978
|
+
btn;
|
|
902
1979
|
|
|
903
1980
|
ul.id = 'medium-editor-toolbar-actions' + this.id;
|
|
904
1981
|
ul.className = 'medium-editor-toolbar-actions clearfix';
|
|
905
1982
|
|
|
906
|
-
|
|
907
|
-
if (
|
|
908
|
-
|
|
909
|
-
btn = ext.getButton !== undefined ? ext.getButton(this) : null;
|
|
910
|
-
if (ext.hasForm) {
|
|
911
|
-
btn.setAttribute('data-form', 'medium-editor-toolbar-form-' + btns[i] + '-' + this.id);
|
|
912
|
-
}
|
|
913
|
-
} else {
|
|
914
|
-
btn = this.buttonTemplate(btns[i]);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (btn) {
|
|
1983
|
+
this.commands.forEach(function (extension) {
|
|
1984
|
+
if (typeof extension.getButton === 'function') {
|
|
1985
|
+
btn = extension.getButton(this);
|
|
918
1986
|
li = this.options.ownerDocument.createElement('li');
|
|
919
|
-
if (isElement(btn)) {
|
|
1987
|
+
if (Util.isElement(btn)) {
|
|
920
1988
|
li.appendChild(btn);
|
|
921
1989
|
} else {
|
|
922
1990
|
li.innerHTML = btn;
|
|
923
1991
|
}
|
|
924
1992
|
ul.appendChild(li);
|
|
925
1993
|
}
|
|
926
|
-
}
|
|
1994
|
+
}.bind(this));
|
|
927
1995
|
|
|
928
1996
|
return ul;
|
|
929
1997
|
},
|
|
930
1998
|
|
|
931
|
-
|
|
932
|
-
var
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (ext.hasForm) {
|
|
942
|
-
form = ext.getForm !== undefined ? ext.getForm() : null;
|
|
943
|
-
}
|
|
944
|
-
if (form) {
|
|
945
|
-
id = 'medium-editor-toolbar-form-' + name + '-' + this.id;
|
|
946
|
-
form.className = 'medium-editor-toolbar-form';
|
|
947
|
-
form.id = id;
|
|
948
|
-
ext.getForm().id = id;
|
|
949
|
-
this.toolbar.appendChild(form);
|
|
1999
|
+
bindSelect: function () {
|
|
2000
|
+
var i,
|
|
2001
|
+
blurHelper = function (event) {
|
|
2002
|
+
// Do not close the toolbar when bluring the editable area and clicking into the anchor form
|
|
2003
|
+
if (event &&
|
|
2004
|
+
event.type &&
|
|
2005
|
+
event.type.toLowerCase() === 'blur' &&
|
|
2006
|
+
event.relatedTarget &&
|
|
2007
|
+
Util.isDescendant(this.toolbar, event.relatedTarget)) {
|
|
2008
|
+
return false;
|
|
950
2009
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
target_label = this.options.ownerDocument.createElement('label'),
|
|
959
|
-
target = this.options.ownerDocument.createElement('input'),
|
|
960
|
-
button_label = this.options.ownerDocument.createElement('label'),
|
|
961
|
-
button = this.options.ownerDocument.createElement('input'),
|
|
962
|
-
close = this.options.ownerDocument.createElement('a'),
|
|
963
|
-
save = this.options.ownerDocument.createElement('a');
|
|
964
|
-
|
|
965
|
-
close.setAttribute('href', '#');
|
|
966
|
-
close.className = 'medium-editor-toobar-close';
|
|
967
|
-
close.innerHTML = '×';
|
|
968
|
-
|
|
969
|
-
save.setAttribute('href', '#');
|
|
970
|
-
save.className = 'medium-editor-toobar-save';
|
|
971
|
-
save.innerHTML = '✓';
|
|
2010
|
+
this.checkSelection();
|
|
2011
|
+
}.bind(this),
|
|
2012
|
+
timeoutHelper = function () {
|
|
2013
|
+
setTimeout(function () {
|
|
2014
|
+
this.checkSelection();
|
|
2015
|
+
}.bind(this), 0);
|
|
2016
|
+
}.bind(this);
|
|
972
2017
|
|
|
973
|
-
|
|
974
|
-
input.className = 'medium-editor-toolbar-input';
|
|
975
|
-
input.setAttribute('placeholder', this.options.anchorInputPlaceholder);
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
target.setAttribute('type', 'checkbox');
|
|
979
|
-
target.className = 'medium-editor-toolbar-anchor-target';
|
|
980
|
-
target_label.innerHTML = this.options.anchorInputCheckboxLabel;
|
|
981
|
-
target_label.insertBefore(target, target_label.firstChild);
|
|
2018
|
+
this.on(this.options.ownerDocument.documentElement, 'mouseup', this.checkSelection.bind(this));
|
|
982
2019
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
anchor.className = 'medium-editor-toolbar-form';
|
|
990
|
-
anchor.id = 'medium-editor-toolbar-form-anchor-' + this.id;
|
|
991
|
-
anchor.appendChild(input);
|
|
2020
|
+
for (i = 0; i < this.elements.length; i += 1) {
|
|
2021
|
+
this.on(this.elements[i], 'keyup', this.checkSelection.bind(this));
|
|
2022
|
+
this.on(this.elements[i], 'blur', blurHelper);
|
|
2023
|
+
this.on(this.elements[i], 'click', timeoutHelper);
|
|
2024
|
+
}
|
|
992
2025
|
|
|
993
|
-
|
|
994
|
-
|
|
2026
|
+
return this;
|
|
2027
|
+
},
|
|
995
2028
|
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
}
|
|
2029
|
+
bindDragDrop: function () {
|
|
2030
|
+
var self = this, i, className, onDrag, onDrop, element;
|
|
999
2031
|
|
|
1000
|
-
if (
|
|
1001
|
-
|
|
2032
|
+
if (!self.options.imageDragging) {
|
|
2033
|
+
return this;
|
|
1002
2034
|
}
|
|
1003
2035
|
|
|
1004
|
-
|
|
1005
|
-
},
|
|
2036
|
+
className = 'medium-editor-dragover';
|
|
1006
2037
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
2038
|
+
onDrag = function (e) {
|
|
2039
|
+
e.preventDefault();
|
|
2040
|
+
e.dataTransfer.dropEffect = "copy";
|
|
1010
2041
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
2042
|
+
if (e.type === "dragover") {
|
|
2043
|
+
this.classList.add(className);
|
|
2044
|
+
} else {
|
|
2045
|
+
this.classList.remove(className);
|
|
1015
2046
|
}
|
|
1016
|
-
|
|
1017
|
-
self.checkSelection();
|
|
1018
2047
|
};
|
|
1019
2048
|
|
|
1020
|
-
|
|
2049
|
+
onDrop = function (e) {
|
|
2050
|
+
var files;
|
|
2051
|
+
e.preventDefault();
|
|
2052
|
+
e.stopPropagation();
|
|
2053
|
+
files = Array.prototype.slice.call(e.dataTransfer.files, 0);
|
|
2054
|
+
files.some(function (file) {
|
|
2055
|
+
if (file.type.match("image")) {
|
|
2056
|
+
var fileReader, id;
|
|
2057
|
+
fileReader = new FileReader();
|
|
2058
|
+
fileReader.readAsDataURL(file);
|
|
2059
|
+
|
|
2060
|
+
id = 'medium-img-' + (+new Date());
|
|
2061
|
+
Util.insertHTMLCommand(self.options.ownerDocument, '<img class="medium-image-loading" id="' + id + '" />');
|
|
2062
|
+
|
|
2063
|
+
fileReader.onload = function () {
|
|
2064
|
+
var img = document.getElementById(id);
|
|
2065
|
+
if (img) {
|
|
2066
|
+
img.removeAttribute('id');
|
|
2067
|
+
img.removeAttribute('class');
|
|
2068
|
+
img.src = fileReader.result;
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
this.classList.remove(className);
|
|
2074
|
+
};
|
|
1021
2075
|
|
|
1022
2076
|
for (i = 0; i < this.elements.length; i += 1) {
|
|
1023
|
-
this.
|
|
1024
|
-
|
|
1025
|
-
|
|
2077
|
+
element = this.elements[i];
|
|
2078
|
+
|
|
2079
|
+
|
|
2080
|
+
this.on(element, 'dragover', onDrag);
|
|
2081
|
+
this.on(element, 'dragleave', onDrag);
|
|
2082
|
+
this.on(element, 'drop', onDrop);
|
|
1026
2083
|
}
|
|
1027
2084
|
return this;
|
|
1028
2085
|
},
|
|
@@ -1045,18 +2102,16 @@ if (typeof module === 'object') {
|
|
|
1045
2102
|
|
|
1046
2103
|
newSelection = this.options.contentWindow.getSelection();
|
|
1047
2104
|
if ((!this.options.updateOnEmptySelection && newSelection.toString().trim() === '') ||
|
|
1048
|
-
(this.options.allowMultiParagraphSelection === false && this.
|
|
1049
|
-
|
|
1050
|
-
|
|
2105
|
+
(this.options.allowMultiParagraphSelection === false && this.multipleBlockElementsSelected()) ||
|
|
2106
|
+
Selection.selectionInContentEditableFalse(this.options.contentWindow)) {
|
|
1051
2107
|
if (!this.options.staticToolbar) {
|
|
1052
2108
|
this.hideToolbarActions();
|
|
1053
|
-
} else
|
|
1054
|
-
this.
|
|
1055
|
-
this.showToolbarActions();
|
|
2109
|
+
} else {
|
|
2110
|
+
this.showAndUpdateToolbar();
|
|
1056
2111
|
}
|
|
1057
2112
|
|
|
1058
2113
|
} else {
|
|
1059
|
-
selectionElement =
|
|
2114
|
+
selectionElement = Selection.getSelectionElement(this.options.contentWindow);
|
|
1060
2115
|
if (!selectionElement || selectionElement.getAttribute('data-disable-toolbar')) {
|
|
1061
2116
|
if (!this.options.staticToolbar) {
|
|
1062
2117
|
this.hideToolbarActions();
|
|
@@ -1069,21 +2124,14 @@ if (typeof module === 'object') {
|
|
|
1069
2124
|
return this;
|
|
1070
2125
|
},
|
|
1071
2126
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return false;
|
|
1080
|
-
},
|
|
1081
|
-
|
|
1082
|
-
hasMultiParagraphs: function () {
|
|
1083
|
-
var selectionHtml = getSelectionHtml.call(this).replace(/<[\S]+><\/[\S]+>/gim, ''),
|
|
1084
|
-
hasMultiParagraphs = selectionHtml.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);
|
|
2127
|
+
// Checks for existance of multiple block elements in the current selection
|
|
2128
|
+
multipleBlockElementsSelected: function () {
|
|
2129
|
+
/*jslint regexp: true*/
|
|
2130
|
+
var selectionHtml = Selection.getSelectionHtml.call(this).replace(/<[\S]+><\/[\S]+>/gim, ''),
|
|
2131
|
+
hasMultiParagraphs = selectionHtml.match(/<(p|h[1-6]|blockquote)[^>]*>/g);
|
|
2132
|
+
/*jslint regexp: false*/
|
|
1085
2133
|
|
|
1086
|
-
return
|
|
2134
|
+
return !!hasMultiParagraphs && hasMultiParagraphs.length > 1;
|
|
1087
2135
|
},
|
|
1088
2136
|
|
|
1089
2137
|
checkSelectionElement: function (newSelection, selectionElement) {
|
|
@@ -1113,7 +2161,7 @@ if (typeof module === 'object') {
|
|
|
1113
2161
|
if (this.options.standardizeSelectionStart &&
|
|
1114
2162
|
this.selectionRange.startContainer.nodeValue &&
|
|
1115
2163
|
(this.selectionRange.startOffset === this.selectionRange.startContainer.nodeValue.length)) {
|
|
1116
|
-
adjacentNode = findAdjacentTextNodeWithContent(
|
|
2164
|
+
adjacentNode = Util.findAdjacentTextNodeWithContent(Selection.getSelectionElement(this.options.contentWindow), this.selectionRange.startContainer, this.options.ownerDocument);
|
|
1117
2165
|
if (adjacentNode) {
|
|
1118
2166
|
offset = 0;
|
|
1119
2167
|
while (adjacentNode.nodeValue.substr(offset, 1).trim().length === 0) {
|
|
@@ -1130,9 +2178,7 @@ if (typeof module === 'object') {
|
|
|
1130
2178
|
|
|
1131
2179
|
for (i = 0; i < this.elements.length; i += 1) {
|
|
1132
2180
|
if (this.elements[i] === selectionElement) {
|
|
1133
|
-
this.
|
|
1134
|
-
.setToolbarPosition()
|
|
1135
|
-
.showToolbarActions();
|
|
2181
|
+
this.showAndUpdateToolbar();
|
|
1136
2182
|
return;
|
|
1137
2183
|
}
|
|
1138
2184
|
}
|
|
@@ -1142,101 +2188,95 @@ if (typeof module === 'object') {
|
|
|
1142
2188
|
}
|
|
1143
2189
|
},
|
|
1144
2190
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
return false;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
range = selection.getRangeAt(0);
|
|
1153
|
-
current = range.commonAncestorContainer;
|
|
1154
|
-
|
|
1155
|
-
do {
|
|
1156
|
-
if (current.nodeType === 1) {
|
|
1157
|
-
if (testElementFunction(current)) {
|
|
1158
|
-
return current;
|
|
1159
|
-
}
|
|
1160
|
-
// do not traverse upwards past the nearest containing editor
|
|
1161
|
-
if (current.getAttribute('data-medium-element')) {
|
|
1162
|
-
return false;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
current = current.parentNode;
|
|
1167
|
-
} while (current);
|
|
1168
|
-
|
|
1169
|
-
return false;
|
|
1170
|
-
},
|
|
1171
|
-
|
|
1172
|
-
getSelectionElement: function () {
|
|
1173
|
-
return this.findMatchingSelectionParent(function (el) {
|
|
1174
|
-
return el.getAttribute('data-medium-element');
|
|
1175
|
-
});
|
|
1176
|
-
},
|
|
1177
|
-
|
|
1178
|
-
selectionInContentEditableFalse: function () {
|
|
1179
|
-
return this.findMatchingSelectionParent(function (el) {
|
|
1180
|
-
return (el && el.nodeName !== '#text' && el.getAttribute('contenteditable') === 'false');
|
|
1181
|
-
});
|
|
2191
|
+
showAndUpdateToolbar: function () {
|
|
2192
|
+
this.setToolbarButtonStates()
|
|
2193
|
+
.setToolbarPosition()
|
|
2194
|
+
.showToolbarDefaultActions();
|
|
1182
2195
|
},
|
|
1183
2196
|
|
|
1184
2197
|
setToolbarPosition: function () {
|
|
1185
2198
|
// document.documentElement for IE 9
|
|
1186
2199
|
var scrollTop = (this.options.ownerDocument.documentElement && this.options.ownerDocument.documentElement.scrollTop) || this.options.ownerDocument.body.scrollTop,
|
|
1187
|
-
container = this.elements[0],
|
|
1188
|
-
containerRect = container.getBoundingClientRect(),
|
|
1189
|
-
containerTop = containerRect.top + scrollTop,
|
|
1190
|
-
buttonHeight = 50,
|
|
1191
2200
|
selection = this.options.contentWindow.getSelection(),
|
|
2201
|
+
windowWidth = this.options.contentWindow.innerWidth,
|
|
2202
|
+
container = Selection.getSelectionElement(this.options.contentWindow),
|
|
2203
|
+
buttonHeight = 50,
|
|
2204
|
+
toolbarWidth,
|
|
2205
|
+
toolbarHeight,
|
|
2206
|
+
halfOffsetWidth,
|
|
2207
|
+
defaultLeft,
|
|
2208
|
+
containerRect,
|
|
2209
|
+
containerTop,
|
|
2210
|
+
containerCenter,
|
|
1192
2211
|
range,
|
|
1193
2212
|
boundary,
|
|
1194
2213
|
middleBoundary,
|
|
1195
|
-
|
|
1196
|
-
halfOffsetWidth = this.toolbar.offsetWidth / 2,
|
|
1197
|
-
containerCenter = (containerRect.left + (containerRect.width / 2));
|
|
2214
|
+
targetLeft;
|
|
1198
2215
|
|
|
1199
|
-
|
|
2216
|
+
// If there isn't a valid selection, bail
|
|
2217
|
+
if (!container || !this.options.contentWindow.getSelection().focusNode) {
|
|
1200
2218
|
return this;
|
|
1201
2219
|
}
|
|
1202
2220
|
|
|
1203
|
-
this
|
|
2221
|
+
// If the container isn't part of this medium-editor instance, bail
|
|
2222
|
+
if (this.elements.indexOf(container) === -1) {
|
|
2223
|
+
return this;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// Calculate container dimensions
|
|
2227
|
+
containerRect = container.getBoundingClientRect();
|
|
2228
|
+
containerTop = containerRect.top + scrollTop;
|
|
2229
|
+
containerCenter = (containerRect.left + (containerRect.width / 2));
|
|
2230
|
+
|
|
2231
|
+
// position the toolbar at left 0, so we can get the real width of the toolbar
|
|
2232
|
+
this.toolbar.style.left = '0';
|
|
2233
|
+
toolbarWidth = this.toolbar.offsetWidth;
|
|
2234
|
+
toolbarHeight = this.toolbar.offsetHeight;
|
|
2235
|
+
halfOffsetWidth = toolbarWidth / 2;
|
|
2236
|
+
defaultLeft = this.options.diffLeft - halfOffsetWidth;
|
|
1204
2237
|
|
|
1205
2238
|
if (this.options.staticToolbar) {
|
|
2239
|
+
this.showToolbar();
|
|
1206
2240
|
|
|
1207
2241
|
if (this.options.stickyToolbar) {
|
|
1208
|
-
|
|
1209
2242
|
// If it's beyond the height of the editor, position it at the bottom of the editor
|
|
1210
|
-
if (scrollTop > (containerTop +
|
|
1211
|
-
this.toolbar.style.top = (containerTop +
|
|
2243
|
+
if (scrollTop > (containerTop + container.offsetHeight - toolbarHeight)) {
|
|
2244
|
+
this.toolbar.style.top = (containerTop + container.offsetHeight - toolbarHeight) + 'px';
|
|
2245
|
+
this.toolbar.classList.remove('sticky-toolbar');
|
|
1212
2246
|
|
|
1213
2247
|
// Stick the toolbar to the top of the window
|
|
1214
|
-
} else if (scrollTop > (containerTop -
|
|
2248
|
+
} else if (scrollTop > (containerTop - toolbarHeight)) {
|
|
1215
2249
|
this.toolbar.classList.add('sticky-toolbar');
|
|
1216
2250
|
this.toolbar.style.top = "0px";
|
|
2251
|
+
|
|
1217
2252
|
// Normal static toolbar position
|
|
1218
2253
|
} else {
|
|
1219
2254
|
this.toolbar.classList.remove('sticky-toolbar');
|
|
1220
|
-
this.toolbar.style.top = containerTop -
|
|
2255
|
+
this.toolbar.style.top = containerTop - toolbarHeight + "px";
|
|
1221
2256
|
}
|
|
1222
|
-
|
|
1223
2257
|
} else {
|
|
1224
|
-
this.toolbar.style.top = containerTop -
|
|
2258
|
+
this.toolbar.style.top = containerTop - toolbarHeight + "px";
|
|
1225
2259
|
}
|
|
1226
2260
|
|
|
1227
|
-
if (this.options.toolbarAlign) {
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
2261
|
+
if (this.options.toolbarAlign === 'left') {
|
|
2262
|
+
targetLeft = containerRect.left;
|
|
2263
|
+
} else if (this.options.toolbarAlign === 'center') {
|
|
2264
|
+
targetLeft = containerCenter - halfOffsetWidth;
|
|
2265
|
+
} else if (this.options.toolbarAlign === 'right') {
|
|
2266
|
+
targetLeft = containerRect.right - toolbarWidth;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
if (targetLeft < 0) {
|
|
2270
|
+
targetLeft = 0;
|
|
2271
|
+
} else if ((targetLeft + toolbarWidth) > windowWidth) {
|
|
2272
|
+
targetLeft = windowWidth - toolbarWidth;
|
|
1237
2273
|
}
|
|
1238
2274
|
|
|
2275
|
+
this.toolbar.style.left = targetLeft + 'px';
|
|
2276
|
+
|
|
1239
2277
|
} else if (!selection.isCollapsed) {
|
|
2278
|
+
this.showToolbar();
|
|
2279
|
+
|
|
1240
2280
|
range = selection.getRangeAt(0);
|
|
1241
2281
|
boundary = range.getBoundingClientRect();
|
|
1242
2282
|
middleBoundary = (boundary.left + boundary.right) / 2;
|
|
@@ -1244,16 +2284,16 @@ if (typeof module === 'object') {
|
|
|
1244
2284
|
if (boundary.top < buttonHeight) {
|
|
1245
2285
|
this.toolbar.classList.add('medium-toolbar-arrow-over');
|
|
1246
2286
|
this.toolbar.classList.remove('medium-toolbar-arrow-under');
|
|
1247
|
-
this.toolbar.style.top = buttonHeight + boundary.bottom - this.options.diffTop + this.options.contentWindow.pageYOffset -
|
|
2287
|
+
this.toolbar.style.top = buttonHeight + boundary.bottom - this.options.diffTop + this.options.contentWindow.pageYOffset - toolbarHeight + 'px';
|
|
1248
2288
|
} else {
|
|
1249
2289
|
this.toolbar.classList.add('medium-toolbar-arrow-under');
|
|
1250
2290
|
this.toolbar.classList.remove('medium-toolbar-arrow-over');
|
|
1251
|
-
this.toolbar.style.top = boundary.top + this.options.diffTop + this.options.contentWindow.pageYOffset -
|
|
2291
|
+
this.toolbar.style.top = boundary.top + this.options.diffTop + this.options.contentWindow.pageYOffset - toolbarHeight + 'px';
|
|
1252
2292
|
}
|
|
1253
2293
|
if (middleBoundary < halfOffsetWidth) {
|
|
1254
2294
|
this.toolbar.style.left = defaultLeft + halfOffsetWidth + 'px';
|
|
1255
|
-
} else if ((
|
|
1256
|
-
this.toolbar.style.left =
|
|
2295
|
+
} else if ((windowWidth - middleBoundary) < halfOffsetWidth) {
|
|
2296
|
+
this.toolbar.style.left = windowWidth + defaultLeft - halfOffsetWidth + 'px';
|
|
1257
2297
|
} else {
|
|
1258
2298
|
this.toolbar.style.left = defaultLeft + middleBoundary + 'px';
|
|
1259
2299
|
}
|
|
@@ -1265,21 +2305,57 @@ if (typeof module === 'object') {
|
|
|
1265
2305
|
},
|
|
1266
2306
|
|
|
1267
2307
|
setToolbarButtonStates: function () {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
}
|
|
2308
|
+
this.commands.forEach(function (extension) {
|
|
2309
|
+
if (typeof extension.isActive === 'function') {
|
|
2310
|
+
extension.setInactive();
|
|
2311
|
+
}
|
|
2312
|
+
}.bind(this));
|
|
1273
2313
|
this.checkActiveButtons();
|
|
1274
2314
|
return this;
|
|
1275
2315
|
},
|
|
1276
2316
|
|
|
1277
2317
|
checkActiveButtons: function () {
|
|
1278
2318
|
var elements = Array.prototype.slice.call(this.elements),
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
2319
|
+
manualStateChecks = [],
|
|
2320
|
+
queryState = null,
|
|
2321
|
+
parentNode,
|
|
2322
|
+
checkExtension = function (extension) {
|
|
2323
|
+
if (typeof extension.checkState === 'function') {
|
|
2324
|
+
extension.checkState(parentNode);
|
|
2325
|
+
} else if (typeof extension.isActive === 'function' &&
|
|
2326
|
+
typeof extension.isAlreadyApplied === 'function') {
|
|
2327
|
+
if (!extension.isActive() && extension.isAlreadyApplied(parentNode)) {
|
|
2328
|
+
extension.setActive();
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
};
|
|
2332
|
+
|
|
2333
|
+
if (!this.selectionRange) {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
parentNode = Selection.getSelectedParentElement(this.selectionRange);
|
|
2337
|
+
|
|
2338
|
+
// Loop through all commands
|
|
2339
|
+
this.commands.forEach(function (command) {
|
|
2340
|
+
// For those commands where we can use document.queryCommandState(), do so
|
|
2341
|
+
if (typeof command.queryCommandState === 'function') {
|
|
2342
|
+
queryState = command.queryCommandState();
|
|
2343
|
+
// If queryCommandState returns a valid value, we can trust the browser
|
|
2344
|
+
// and don't need to do our manual checks
|
|
2345
|
+
if (queryState !== null) {
|
|
2346
|
+
if (queryState) {
|
|
2347
|
+
command.setActive();
|
|
2348
|
+
}
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
// We can't use queryCommandState for this command, so add to manualStateChecks
|
|
2353
|
+
manualStateChecks.push(command);
|
|
2354
|
+
});
|
|
2355
|
+
|
|
2356
|
+
// Climb up the DOM and do manual checks for whether a certain command is currently enabled for this node
|
|
2357
|
+
while (parentNode.tagName !== undefined && Util.parentElements.indexOf(parentNode.tagName.toLowerCase) === -1) {
|
|
2358
|
+
manualStateChecks.forEach(checkExtension.bind(this));
|
|
1283
2359
|
|
|
1284
2360
|
// we can abort the search upwards if we leave the contentEditable element
|
|
1285
2361
|
if (elements.indexOf(parentNode) !== -1) {
|
|
@@ -1289,45 +2365,8 @@ if (typeof module === 'object') {
|
|
|
1289
2365
|
}
|
|
1290
2366
|
},
|
|
1291
2367
|
|
|
1292
|
-
|
|
1293
|
-
var
|
|
1294
|
-
if (el !== null && el.className.indexOf(this.options.activeButtonClass) === -1) {
|
|
1295
|
-
el.className += ' ' + this.options.activeButtonClass;
|
|
1296
|
-
}
|
|
1297
|
-
},
|
|
1298
|
-
|
|
1299
|
-
bindButtons: function () {
|
|
1300
|
-
var buttons = this.toolbar.querySelectorAll('button'),
|
|
1301
|
-
i,
|
|
1302
|
-
self = this,
|
|
1303
|
-
triggerAction = function (e) {
|
|
1304
|
-
e.preventDefault();
|
|
1305
|
-
e.stopPropagation();
|
|
1306
|
-
if (self.selection === undefined) {
|
|
1307
|
-
self.checkSelection();
|
|
1308
|
-
}
|
|
1309
|
-
if (this.className.indexOf(self.options.activeButtonClass) > -1) {
|
|
1310
|
-
this.classList.remove(self.options.activeButtonClass);
|
|
1311
|
-
} else {
|
|
1312
|
-
this.className += ' ' + self.options.activeButtonClass;
|
|
1313
|
-
}
|
|
1314
|
-
if (this.hasAttribute('data-action')) {
|
|
1315
|
-
self.execAction(this.getAttribute('data-action'), e);
|
|
1316
|
-
}
|
|
1317
|
-
// Allows extension buttons to show a form
|
|
1318
|
-
// TO DO: Improve this
|
|
1319
|
-
if (this.hasAttribute('data-form')) {
|
|
1320
|
-
self.showForm(this.getAttribute('data-form'), e);
|
|
1321
|
-
}
|
|
1322
|
-
};
|
|
1323
|
-
for (i = 0; i < buttons.length; i += 1) {
|
|
1324
|
-
this.on(buttons[i], 'click', triggerAction);
|
|
1325
|
-
}
|
|
1326
|
-
this.setFirstAndLastItems(buttons);
|
|
1327
|
-
return this;
|
|
1328
|
-
},
|
|
1329
|
-
|
|
1330
|
-
setFirstAndLastItems: function (buttons) {
|
|
2368
|
+
setFirstAndLastButtons: function () {
|
|
2369
|
+
var buttons = this.toolbar.querySelectorAll('button');
|
|
1331
2370
|
if (buttons.length > 0) {
|
|
1332
2371
|
buttons[0].className += ' ' + this.options.firstButtonClass;
|
|
1333
2372
|
buttons[buttons.length - 1].className += ' ' + this.options.lastButtonClass;
|
|
@@ -1335,82 +2374,84 @@ if (typeof module === 'object') {
|
|
|
1335
2374
|
return this;
|
|
1336
2375
|
},
|
|
1337
2376
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
} else {
|
|
1350
|
-
this.options.ownerDocument.execCommand(action, false, null);
|
|
1351
|
-
this.setToolbarPosition();
|
|
2377
|
+
// Wrapper around document.queryCommandState for checking whether an action has already
|
|
2378
|
+
// been applied to the current selection
|
|
2379
|
+
queryCommandState: function (action) {
|
|
2380
|
+
var fullAction = /^full-(.+)$/gi,
|
|
2381
|
+
match,
|
|
2382
|
+
queryState = null;
|
|
2383
|
+
|
|
2384
|
+
// Actions starting with 'full-' need to be modified since this is a medium-editor concept
|
|
2385
|
+
match = fullAction.exec(action);
|
|
2386
|
+
if (match) {
|
|
2387
|
+
action = match[1];
|
|
1352
2388
|
}
|
|
1353
|
-
},
|
|
1354
2389
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
var form = document.getElementById(formId);
|
|
1361
|
-
form.style.display = 'block';
|
|
1362
|
-
this.setToolbarPosition();
|
|
1363
|
-
this.keepToolbarAlive = true;
|
|
1364
|
-
},
|
|
2390
|
+
try {
|
|
2391
|
+
queryState = this.options.ownerDocument.queryCommandState(action);
|
|
2392
|
+
} catch (exc) {
|
|
2393
|
+
queryState = null;
|
|
2394
|
+
}
|
|
1365
2395
|
|
|
1366
|
-
|
|
1367
|
-
// TO DO: Improve this
|
|
1368
|
-
hideForm: function (form, e) {
|
|
1369
|
-
var el = document.getElementById(form.id);
|
|
1370
|
-
el.style.display = 'none';
|
|
1371
|
-
this.showToolbarActions();
|
|
1372
|
-
this.setToolbarPosition();
|
|
1373
|
-
restoreSelection.call(this, this.savedSelection);
|
|
2396
|
+
return queryState;
|
|
1374
2397
|
},
|
|
1375
2398
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
var
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
},
|
|
2399
|
+
execAction: function (action, opts) {
|
|
2400
|
+
/*jslint regexp: true*/
|
|
2401
|
+
var fullAction = /^full-(.+)$/gi,
|
|
2402
|
+
match,
|
|
2403
|
+
result;
|
|
2404
|
+
/*jslint regexp: false*/
|
|
1383
2405
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
if (
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
2406
|
+
// Actions starting with 'full-' should be applied to to the entire contents of the editable element
|
|
2407
|
+
// (ie full-bold, full-append-pre, etc.)
|
|
2408
|
+
match = fullAction.exec(action);
|
|
2409
|
+
if (match) {
|
|
2410
|
+
// Store the current selection to be restored after applying the action
|
|
2411
|
+
this.saveSelection();
|
|
2412
|
+
// Select all of the contents before calling the action
|
|
2413
|
+
this.selectAllContents();
|
|
2414
|
+
result = this.execActionInternal(match[1], opts);
|
|
2415
|
+
// Restore the previous selection
|
|
2416
|
+
this.restoreSelection();
|
|
1391
2417
|
} else {
|
|
1392
|
-
|
|
2418
|
+
result = this.execActionInternal(action, opts);
|
|
1393
2419
|
}
|
|
1394
|
-
|
|
2420
|
+
|
|
2421
|
+
this.checkSelection();
|
|
2422
|
+
return result;
|
|
1395
2423
|
},
|
|
1396
2424
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
2425
|
+
execActionInternal: function (action, opts) {
|
|
2426
|
+
/*jslint regexp: true*/
|
|
2427
|
+
var appendAction = /^append-(.+)$/gi,
|
|
2428
|
+
match;
|
|
2429
|
+
/*jslint regexp: false*/
|
|
2430
|
+
|
|
2431
|
+
// Actions starting with 'append-' should attempt to format a block of text ('formatBlock') using a specific
|
|
2432
|
+
// type of block element (ie append-blockquote, append-h1, append-pre, etc.)
|
|
2433
|
+
match = appendAction.exec(action);
|
|
2434
|
+
if (match) {
|
|
2435
|
+
return this.execFormatBlock(match[1]);
|
|
1408
2436
|
}
|
|
1409
|
-
|
|
2437
|
+
|
|
2438
|
+
if (action === 'createLink') {
|
|
2439
|
+
return this.createLink(opts);
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
if (action === 'image') {
|
|
2443
|
+
return this.options.ownerDocument.execCommand('insertImage', false, this.options.contentWindow.getSelection());
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
return this.options.ownerDocument.execCommand(action, false, null);
|
|
2447
|
+
},
|
|
2448
|
+
|
|
2449
|
+
getSelectedParentElement: function () {
|
|
2450
|
+
return Selection.getSelectedParentElement();
|
|
1410
2451
|
},
|
|
1411
2452
|
|
|
1412
2453
|
execFormatBlock: function (el) {
|
|
1413
|
-
var selectionData =
|
|
2454
|
+
var selectionData = Selection.getSelectionData(this.selection.anchorNode);
|
|
1414
2455
|
// FF handles blockquote differently on formatBlock
|
|
1415
2456
|
// allowing nesting, we need to use outdent
|
|
1416
2457
|
// https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla
|
|
@@ -1425,7 +2466,7 @@ if (typeof module === 'object') {
|
|
|
1425
2466
|
// blockquote needs to be called as indent
|
|
1426
2467
|
// http://stackoverflow.com/questions/10741831/execcommand-formatblock-headings-in-ie
|
|
1427
2468
|
// http://stackoverflow.com/questions/1816223/rich-text-editor-with-blockquote-function/1821777#1821777
|
|
1428
|
-
if (
|
|
2469
|
+
if (Util.isIE) {
|
|
1429
2470
|
if (el === 'blockquote') {
|
|
1430
2471
|
return this.options.ownerDocument.execCommand('indent', false, el);
|
|
1431
2472
|
}
|
|
@@ -1434,32 +2475,45 @@ if (typeof module === 'object') {
|
|
|
1434
2475
|
return this.options.ownerDocument.execCommand('formatBlock', false, el);
|
|
1435
2476
|
},
|
|
1436
2477
|
|
|
1437
|
-
|
|
1438
|
-
|
|
2478
|
+
isToolbarDefaultActionsShown: function () {
|
|
2479
|
+
return !!this.toolbarActions && this.toolbarActions.style.display === 'block';
|
|
2480
|
+
},
|
|
1439
2481
|
|
|
1440
|
-
|
|
1441
|
-
|
|
2482
|
+
hideToolbarDefaultActions: function () {
|
|
2483
|
+
if (this.toolbarActions && this.isToolbarDefaultActionsShown()) {
|
|
2484
|
+
this.commands.forEach(function (extension) {
|
|
2485
|
+
if (extension.onHide && typeof extension.onHide === 'function') {
|
|
2486
|
+
extension.onHide();
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
this.toolbarActions.style.display = 'none';
|
|
1442
2490
|
}
|
|
2491
|
+
},
|
|
1443
2492
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
2493
|
+
showToolbarDefaultActions: function () {
|
|
2494
|
+
this.hideExtensionForms();
|
|
2495
|
+
|
|
2496
|
+
if (this.toolbarActions && !this.isToolbarDefaultActionsShown()) {
|
|
2497
|
+
this.toolbarActions.style.display = 'block';
|
|
1449
2498
|
}
|
|
1450
2499
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
2500
|
+
this.keepToolbarAlive = false;
|
|
2501
|
+
// Using setTimeout + options.delay because:
|
|
2502
|
+
// We will actually be displaying the toolbar, which should be controlled by options.delay
|
|
2503
|
+
this.delay(function () {
|
|
2504
|
+
this.showToolbar();
|
|
2505
|
+
}.bind(this));
|
|
2506
|
+
|
|
2507
|
+
return this;
|
|
1455
2508
|
},
|
|
1456
2509
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
2510
|
+
hideExtensionForms: function () {
|
|
2511
|
+
// Hide all extension forms
|
|
2512
|
+
this.commands.forEach(function (extension) {
|
|
2513
|
+
if (extension.hasForm && extension.isDisplayed()) {
|
|
2514
|
+
extension.hideForm();
|
|
2515
|
+
}
|
|
2516
|
+
});
|
|
1463
2517
|
},
|
|
1464
2518
|
|
|
1465
2519
|
isToolbarShown: function () {
|
|
@@ -1469,8 +2523,8 @@ if (typeof module === 'object') {
|
|
|
1469
2523
|
showToolbar: function () {
|
|
1470
2524
|
if (this.toolbar && !this.isToolbarShown()) {
|
|
1471
2525
|
this.toolbar.classList.add('medium-editor-toolbar-active');
|
|
1472
|
-
if (this.onShowToolbar) {
|
|
1473
|
-
this.onShowToolbar();
|
|
2526
|
+
if (typeof this.options.onShowToolbar === 'function') {
|
|
2527
|
+
this.options.onShowToolbar();
|
|
1474
2528
|
}
|
|
1475
2529
|
}
|
|
1476
2530
|
},
|
|
@@ -1478,134 +2532,127 @@ if (typeof module === 'object') {
|
|
|
1478
2532
|
hideToolbar: function () {
|
|
1479
2533
|
if (this.isToolbarShown()) {
|
|
1480
2534
|
this.toolbar.classList.remove('medium-editor-toolbar-active');
|
|
1481
|
-
if (this.onHideToolbar) {
|
|
1482
|
-
this.onHideToolbar();
|
|
2535
|
+
if (typeof this.options.onHideToolbar === 'function') {
|
|
2536
|
+
this.options.onHideToolbar();
|
|
1483
2537
|
}
|
|
1484
2538
|
}
|
|
1485
2539
|
},
|
|
1486
2540
|
|
|
1487
2541
|
hideToolbarActions: function () {
|
|
2542
|
+
this.commands.forEach(function (extension) {
|
|
2543
|
+
if (extension.onHide && typeof extension.onHide === 'function') {
|
|
2544
|
+
extension.onHide();
|
|
2545
|
+
}
|
|
2546
|
+
});
|
|
1488
2547
|
this.keepToolbarAlive = false;
|
|
1489
2548
|
this.hideToolbar();
|
|
1490
2549
|
},
|
|
1491
2550
|
|
|
1492
|
-
|
|
1493
|
-
var
|
|
1494
|
-
|
|
1495
|
-
this.
|
|
1496
|
-
}
|
|
1497
|
-
this.toolbarActions.style.display = 'block';
|
|
1498
|
-
this.keepToolbarAlive = false;
|
|
1499
|
-
// Using setTimeout + options.delay because:
|
|
1500
|
-
// We will actually be displaying the toolbar, which should be controlled by options.delay
|
|
1501
|
-
this.delay(function () {
|
|
1502
|
-
self.showToolbar();
|
|
1503
|
-
});
|
|
1504
|
-
},
|
|
1505
|
-
|
|
1506
|
-
saveSelection: function () {
|
|
1507
|
-
this.savedSelection = saveSelection.call(this);
|
|
1508
|
-
},
|
|
1509
|
-
|
|
1510
|
-
restoreSelection: function () {
|
|
1511
|
-
restoreSelection.call(this, this.savedSelection);
|
|
1512
|
-
},
|
|
1513
|
-
|
|
1514
|
-
showAnchorForm: function (link_value) {
|
|
1515
|
-
if (!this.anchorForm) {
|
|
1516
|
-
return;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
this.toolbarActions.style.display = 'none';
|
|
1520
|
-
this.saveSelection();
|
|
1521
|
-
this.anchorForm.style.display = 'block';
|
|
1522
|
-
this.setToolbarPosition();
|
|
1523
|
-
this.keepToolbarAlive = true;
|
|
1524
|
-
this.anchorInput.focus();
|
|
1525
|
-
this.anchorInput.value = link_value || '';
|
|
1526
|
-
},
|
|
2551
|
+
selectAllContents: function () {
|
|
2552
|
+
var range = this.options.ownerDocument.createRange(),
|
|
2553
|
+
sel = this.options.contentWindow.getSelection(),
|
|
2554
|
+
currNode = Selection.getSelectionElement(this.options.contentWindow);
|
|
1527
2555
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
2556
|
+
if (currNode) {
|
|
2557
|
+
// Move to the lowest descendant node that still selects all of the contents
|
|
2558
|
+
while (currNode.children.length === 1) {
|
|
2559
|
+
currNode = currNode.children[0];
|
|
2560
|
+
}
|
|
1532
2561
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
2562
|
+
range.selectNodeContents(currNode);
|
|
2563
|
+
sel.removeAllRanges();
|
|
2564
|
+
sel.addRange(range);
|
|
2565
|
+
}
|
|
2566
|
+
},
|
|
1536
2567
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
2568
|
+
// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
|
|
2569
|
+
// Tim Down
|
|
2570
|
+
// TODO: move to selection.js and clean up old methods there
|
|
2571
|
+
saveSelection: function () {
|
|
2572
|
+
this.selectionState = null;
|
|
1541
2573
|
|
|
1542
|
-
this.
|
|
1543
|
-
|
|
1544
|
-
|
|
2574
|
+
var selection = this.options.contentWindow.getSelection(),
|
|
2575
|
+
range,
|
|
2576
|
+
preSelectionRange,
|
|
2577
|
+
start,
|
|
2578
|
+
editableElementIndex = -1;
|
|
1545
2579
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
target = "_blank";
|
|
1550
|
-
} else {
|
|
1551
|
-
target = "_self";
|
|
1552
|
-
}
|
|
2580
|
+
if (selection.rangeCount > 0) {
|
|
2581
|
+
range = selection.getRangeAt(0);
|
|
2582
|
+
preSelectionRange = range.cloneRange();
|
|
1553
2583
|
|
|
1554
|
-
|
|
1555
|
-
|
|
2584
|
+
// Find element current selection is inside
|
|
2585
|
+
this.elements.forEach(function (el, index) {
|
|
2586
|
+
if (el === range.startContainer || Util.isDescendant(el, range.startContainer)) {
|
|
2587
|
+
editableElementIndex = index;
|
|
2588
|
+
return false;
|
|
1556
2589
|
}
|
|
2590
|
+
});
|
|
1557
2591
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
restoreSelection.call(self, self.savedSelection);
|
|
1563
|
-
}
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
this.on(linkSave, 'click', function (e) {
|
|
1567
|
-
var button = null,
|
|
1568
|
-
target;
|
|
1569
|
-
e.preventDefault();
|
|
1570
|
-
if (self.options.anchorTarget && self.anchorTarget.checked) {
|
|
1571
|
-
target = "_blank";
|
|
1572
|
-
} else {
|
|
1573
|
-
target = "_self";
|
|
1574
|
-
}
|
|
2592
|
+
if (editableElementIndex > -1) {
|
|
2593
|
+
preSelectionRange.selectNodeContents(this.elements[editableElementIndex]);
|
|
2594
|
+
preSelectionRange.setEnd(range.startContainer, range.startOffset);
|
|
2595
|
+
start = preSelectionRange.toString().length;
|
|
1575
2596
|
|
|
1576
|
-
|
|
1577
|
-
|
|
2597
|
+
this.selectionState = {
|
|
2598
|
+
start: start,
|
|
2599
|
+
end: start + range.toString().length,
|
|
2600
|
+
editableElementIndex: editableElementIndex
|
|
2601
|
+
};
|
|
1578
2602
|
}
|
|
2603
|
+
}
|
|
2604
|
+
},
|
|
1579
2605
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
});
|
|
2606
|
+
// http://stackoverflow.com/questions/17678843/cant-restore-selection-after-html-modify-even-if-its-the-same-html
|
|
2607
|
+
// Tim Down
|
|
2608
|
+
// TODO: move to selection.js and clean up old methods there
|
|
2609
|
+
restoreSelection: function () {
|
|
2610
|
+
if (!this.selectionState) {
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
1588
2613
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
2614
|
+
var editableElement = this.elements[this.selectionState.editableElementIndex],
|
|
2615
|
+
charIndex = 0,
|
|
2616
|
+
range = this.options.ownerDocument.createRange(),
|
|
2617
|
+
nodeStack = [editableElement],
|
|
2618
|
+
node,
|
|
2619
|
+
foundStart = false,
|
|
2620
|
+
stop = false,
|
|
2621
|
+
i,
|
|
2622
|
+
sel,
|
|
2623
|
+
nextCharIndex;
|
|
2624
|
+
|
|
2625
|
+
range.setStart(editableElement, 0);
|
|
2626
|
+
range.collapse(true);
|
|
2627
|
+
|
|
2628
|
+
node = nodeStack.pop();
|
|
2629
|
+
while (!stop && node) {
|
|
2630
|
+
if (node.nodeType === 3) {
|
|
2631
|
+
nextCharIndex = charIndex + node.length;
|
|
2632
|
+
if (!foundStart && this.selectionState.start >= charIndex && this.selectionState.start <= nextCharIndex) {
|
|
2633
|
+
range.setStart(node, this.selectionState.start - charIndex);
|
|
2634
|
+
foundStart = true;
|
|
2635
|
+
}
|
|
2636
|
+
if (foundStart && this.selectionState.end >= charIndex && this.selectionState.end <= nextCharIndex) {
|
|
2637
|
+
range.setEnd(node, this.selectionState.end - charIndex);
|
|
2638
|
+
stop = true;
|
|
2639
|
+
}
|
|
2640
|
+
charIndex = nextCharIndex;
|
|
2641
|
+
} else {
|
|
2642
|
+
i = node.childNodes.length - 1;
|
|
2643
|
+
while (i >= 0) {
|
|
2644
|
+
nodeStack.push(node.childNodes[i]);
|
|
2645
|
+
i -= 1;
|
|
2646
|
+
}
|
|
1594
2647
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (e.target !== self.anchorForm && !isDescendant(self.anchorForm, e.target) && !isDescendant(self.toolbarActions, e.target)) {
|
|
1598
|
-
self.keepToolbarAlive = false;
|
|
1599
|
-
self.checkSelection();
|
|
2648
|
+
if (!stop) {
|
|
2649
|
+
node = nodeStack.pop();
|
|
1600
2650
|
}
|
|
1601
|
-
}
|
|
2651
|
+
}
|
|
1602
2652
|
|
|
1603
|
-
this.
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
restoreSelection.call(self, self.savedSelection);
|
|
1607
|
-
});
|
|
1608
|
-
return this;
|
|
2653
|
+
sel = this.options.contentWindow.getSelection();
|
|
2654
|
+
sel.removeAllRanges();
|
|
2655
|
+
sel.addRange(range);
|
|
1609
2656
|
},
|
|
1610
2657
|
|
|
1611
2658
|
hideAnchorPreview: function () {
|
|
@@ -1626,7 +2673,7 @@ if (typeof module === 'object') {
|
|
|
1626
2673
|
halfOffsetWidth,
|
|
1627
2674
|
defaultLeft;
|
|
1628
2675
|
|
|
1629
|
-
self.anchorPreview.querySelector('i').textContent = anchorEl.href;
|
|
2676
|
+
self.anchorPreview.querySelector('i').textContent = anchorEl.attributes.href.value;
|
|
1630
2677
|
halfOffsetWidth = self.anchorPreview.offsetWidth / 2;
|
|
1631
2678
|
defaultLeft = self.options.diffLeft - halfOffsetWidth;
|
|
1632
2679
|
|
|
@@ -1711,25 +2758,26 @@ if (typeof module === 'object') {
|
|
|
1711
2758
|
'</div>';
|
|
1712
2759
|
},
|
|
1713
2760
|
|
|
1714
|
-
anchorPreviewClickHandler: function (
|
|
1715
|
-
|
|
2761
|
+
anchorPreviewClickHandler: function (event) {
|
|
2762
|
+
var range,
|
|
2763
|
+
sel,
|
|
2764
|
+
anchorExtension = this.getExtensionByName('anchor');
|
|
1716
2765
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
2766
|
+
if (anchorExtension && this.activeAnchor) {
|
|
2767
|
+
range = this.options.ownerDocument.createRange();
|
|
2768
|
+
range.selectNodeContents(this.activeAnchor);
|
|
1720
2769
|
|
|
1721
|
-
|
|
2770
|
+
sel = this.options.contentWindow.getSelection();
|
|
1722
2771
|
sel.removeAllRanges();
|
|
1723
2772
|
sel.addRange(range);
|
|
1724
2773
|
// Using setTimeout + options.delay because:
|
|
1725
|
-
// We may actually be displaying the anchor
|
|
2774
|
+
// We may actually be displaying the anchor form, which should be controlled by options.delay
|
|
1726
2775
|
this.delay(function () {
|
|
1727
|
-
if (
|
|
1728
|
-
|
|
2776
|
+
if (this.activeAnchor) {
|
|
2777
|
+
anchorExtension.showForm(this.activeAnchor.attributes.href.value);
|
|
1729
2778
|
}
|
|
1730
|
-
|
|
1731
|
-
});
|
|
1732
|
-
|
|
2779
|
+
this.keepToolbarAlive = false;
|
|
2780
|
+
}.bind(this));
|
|
1733
2781
|
}
|
|
1734
2782
|
|
|
1735
2783
|
this.hideAnchorPreview();
|
|
@@ -1782,27 +2830,33 @@ if (typeof module === 'object') {
|
|
|
1782
2830
|
return this;
|
|
1783
2831
|
},
|
|
1784
2832
|
|
|
1785
|
-
|
|
1786
|
-
var
|
|
1787
|
-
|
|
1788
|
-
},
|
|
2833
|
+
createLink: function (opts) {
|
|
2834
|
+
var customEvent,
|
|
2835
|
+
i;
|
|
1789
2836
|
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
el = el || getSelectionStart.call(this);
|
|
1793
|
-
if (el.tagName.toLowerCase() === 'a') {
|
|
1794
|
-
el.target = '_blank';
|
|
1795
|
-
} else {
|
|
1796
|
-
el = el.getElementsByTagName('a');
|
|
2837
|
+
if (opts.url && opts.url.trim().length > 0) {
|
|
2838
|
+
this.options.ownerDocument.execCommand('createLink', false, opts.url);
|
|
1797
2839
|
|
|
1798
|
-
|
|
1799
|
-
|
|
2840
|
+
if (this.options.targetBlank || opts.target === '_blank') {
|
|
2841
|
+
Util.setTargetBlank(Selection.getSelectionStart(this.options.ownerDocument));
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
if (opts.buttonClass) {
|
|
2845
|
+
this.setButtonClass(opts.buttonClass);
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
if (this.options.targetBlank || opts.target === "_blank" || opts.buttonClass) {
|
|
2850
|
+
customEvent = this.options.ownerDocument.createEvent("HTMLEvents");
|
|
2851
|
+
customEvent.initEvent("input", true, true, this.options.contentWindow);
|
|
2852
|
+
for (i = 0; i < this.elements.length; i += 1) {
|
|
2853
|
+
this.elements[i].dispatchEvent(customEvent);
|
|
1800
2854
|
}
|
|
1801
2855
|
}
|
|
1802
2856
|
},
|
|
1803
2857
|
|
|
1804
2858
|
setButtonClass: function (buttonClass) {
|
|
1805
|
-
var el = getSelectionStart
|
|
2859
|
+
var el = Selection.getSelectionStart(this.options.ownerDocument),
|
|
1806
2860
|
classes = buttonClass.split(' '),
|
|
1807
2861
|
i,
|
|
1808
2862
|
j;
|
|
@@ -1820,47 +2874,6 @@ if (typeof module === 'object') {
|
|
|
1820
2874
|
}
|
|
1821
2875
|
},
|
|
1822
2876
|
|
|
1823
|
-
createLink: function (input, target, buttonClass) {
|
|
1824
|
-
var i, event;
|
|
1825
|
-
|
|
1826
|
-
this.createLinkInternal(input.value, target, buttonClass);
|
|
1827
|
-
|
|
1828
|
-
if (this.options.targetBlank || target === "_blank" || buttonClass) {
|
|
1829
|
-
event = this.options.ownerDocument.createEvent("HTMLEvents");
|
|
1830
|
-
event.initEvent("input", true, true, this.options.contentWindow);
|
|
1831
|
-
for (i = 0; i < this.elements.length; i += 1) {
|
|
1832
|
-
this.elements[i].dispatchEvent(event);
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
this.checkSelection();
|
|
1837
|
-
this.showToolbarActions();
|
|
1838
|
-
input.value = '';
|
|
1839
|
-
},
|
|
1840
|
-
|
|
1841
|
-
createLinkInternal: function (url, target, buttonClass) {
|
|
1842
|
-
if (!url || url.trim().length === 0) {
|
|
1843
|
-
this.hideToolbarActions();
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
|
|
1847
|
-
restoreSelection.call(this, this.savedSelection);
|
|
1848
|
-
|
|
1849
|
-
if (this.options.checkLinkFormat) {
|
|
1850
|
-
url = this.checkLinkFormat(url);
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
this.options.ownerDocument.execCommand('createLink', false, url);
|
|
1854
|
-
|
|
1855
|
-
if (this.options.targetBlank || target === "_blank") {
|
|
1856
|
-
this.setTargetBlank();
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
if (buttonClass) {
|
|
1860
|
-
this.setButtonClass(buttonClass);
|
|
1861
|
-
}
|
|
1862
|
-
},
|
|
1863
|
-
|
|
1864
2877
|
positionToolbarIfShown: function () {
|
|
1865
2878
|
if (this.isToolbarShown()) {
|
|
1866
2879
|
this.setToolbarPosition();
|
|
@@ -1881,6 +2894,9 @@ if (typeof module === 'object') {
|
|
|
1881
2894
|
this.on(this.options.contentWindow, 'resize', function () {
|
|
1882
2895
|
self.handleResize();
|
|
1883
2896
|
});
|
|
2897
|
+
|
|
2898
|
+
this.bindBlur();
|
|
2899
|
+
|
|
1884
2900
|
return this;
|
|
1885
2901
|
},
|
|
1886
2902
|
|
|
@@ -1912,59 +2928,19 @@ if (typeof module === 'object') {
|
|
|
1912
2928
|
this.elements[i].removeAttribute('data-medium-element');
|
|
1913
2929
|
}
|
|
1914
2930
|
|
|
1915
|
-
this.
|
|
1916
|
-
|
|
2931
|
+
this.commands.forEach(function (extension) {
|
|
2932
|
+
if (typeof extension.deactivate === 'function') {
|
|
2933
|
+
extension.deactivate();
|
|
2934
|
+
}
|
|
2935
|
+
}.bind(this));
|
|
1917
2936
|
|
|
1918
|
-
|
|
1919
|
-
// converts special characters (like <) into their escaped/encoded values (like <).
|
|
1920
|
-
// This allows you to show to display the string without the browser reading it as HTML.
|
|
1921
|
-
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
2937
|
+
this.removeAllEvents();
|
|
1922
2938
|
},
|
|
1923
2939
|
|
|
1924
2940
|
bindPaste: function () {
|
|
1925
2941
|
var i, self = this;
|
|
1926
2942
|
this.pasteWrapper = function (e) {
|
|
1927
|
-
|
|
1928
|
-
html = '',
|
|
1929
|
-
p,
|
|
1930
|
-
dataFormatHTML = 'text/html',
|
|
1931
|
-
dataFormatPlain = 'text/plain';
|
|
1932
|
-
|
|
1933
|
-
this.classList.remove('medium-editor-placeholder');
|
|
1934
|
-
if (!self.options.forcePlainText && !self.options.cleanPastedHTML) {
|
|
1935
|
-
return this;
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
if (self.options.contentWindow.clipboardData && e.clipboardData === undefined) {
|
|
1939
|
-
e.clipboardData = self.options.contentWindow.clipboardData;
|
|
1940
|
-
// If window.clipboardData exists, but e.clipboardData doesn't exist,
|
|
1941
|
-
// we're probably in IE. IE only has two possibilities for clipboard
|
|
1942
|
-
// data format: 'Text' and 'URL'.
|
|
1943
|
-
//
|
|
1944
|
-
// Of the two, we want 'Text':
|
|
1945
|
-
dataFormatHTML = 'Text';
|
|
1946
|
-
dataFormatPlain = 'Text';
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
if (e.clipboardData && e.clipboardData.getData && !e.defaultPrevented) {
|
|
1950
|
-
e.preventDefault();
|
|
1951
|
-
|
|
1952
|
-
if (self.options.cleanPastedHTML && e.clipboardData.getData(dataFormatHTML)) {
|
|
1953
|
-
return self.cleanPaste(e.clipboardData.getData(dataFormatHTML));
|
|
1954
|
-
}
|
|
1955
|
-
if (!(self.options.disableReturn || this.getAttribute('data-disable-return'))) {
|
|
1956
|
-
paragraphs = e.clipboardData.getData(dataFormatPlain).split(/[\r\n]/g);
|
|
1957
|
-
for (p = 0; p < paragraphs.length; p += 1) {
|
|
1958
|
-
if (paragraphs[p] !== '') {
|
|
1959
|
-
html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>';
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
insertHTMLCommand(self.options.ownerDocument, html);
|
|
1963
|
-
} else {
|
|
1964
|
-
html = self.htmlEntities(e.clipboardData.getData(dataFormatPlain));
|
|
1965
|
-
insertHTMLCommand(self.options.ownerDocument, html);
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
2943
|
+
pasteHandler.handlePaste(this, e, self.options);
|
|
1968
2944
|
};
|
|
1969
2945
|
for (i = 0; i < this.elements.length; i += 1) {
|
|
1970
2946
|
this.on(this.elements[i], 'paste', this.pasteWrapper);
|
|
@@ -1985,197 +2961,15 @@ if (typeof module === 'object') {
|
|
|
1985
2961
|
},
|
|
1986
2962
|
|
|
1987
2963
|
cleanPaste: function (text) {
|
|
1988
|
-
|
|
1989
|
-
/*jslint regexp: true*/
|
|
1990
|
-
/*
|
|
1991
|
-
jslint does not allow character negation, because the negation
|
|
1992
|
-
will not match any unicode characters. In the regexes in this
|
|
1993
|
-
block, negation is used specifically to match the end of an html
|
|
1994
|
-
tag, and in fact unicode characters *should* be allowed.
|
|
1995
|
-
*/
|
|
1996
|
-
var i, elList, workEl,
|
|
1997
|
-
el = this.getSelectionElement(),
|
|
1998
|
-
multiline = /<p|<br|<div/.test(text),
|
|
1999
|
-
replacements = [
|
|
2000
|
-
|
|
2001
|
-
// replace two bogus tags that begin pastes from google docs
|
|
2002
|
-
[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ""],
|
|
2003
|
-
[new RegExp(/<\/b>(<br[^>]*>)?$/gi), ""],
|
|
2004
|
-
|
|
2005
|
-
// un-html spaces and newlines inserted by OS X
|
|
2006
|
-
[new RegExp(/<span class="Apple-converted-space">\s+<\/span>/g), ' '],
|
|
2007
|
-
[new RegExp(/<br class="Apple-interchange-newline">/g), '<br>'],
|
|
2008
|
-
|
|
2009
|
-
// replace google docs italics+bold with a span to be replaced once the html is inserted
|
|
2010
|
-
[new RegExp(/<span[^>]*(font-style:italic;font-weight:bold|font-weight:bold;font-style:italic)[^>]*>/gi), '<span class="replace-with italic bold">'],
|
|
2011
|
-
|
|
2012
|
-
// replace google docs italics with a span to be replaced once the html is inserted
|
|
2013
|
-
[new RegExp(/<span[^>]*font-style:italic[^>]*>/gi), '<span class="replace-with italic">'],
|
|
2014
|
-
|
|
2015
|
-
//[replace google docs bolds with a span to be replaced once the html is inserted
|
|
2016
|
-
[new RegExp(/<span[^>]*font-weight:bold[^>]*>/gi), '<span class="replace-with bold">'],
|
|
2017
|
-
|
|
2018
|
-
// replace manually entered b/i/a tags with real ones
|
|
2019
|
-
[new RegExp(/<(\/?)(i|b|a)>/gi), '<$1$2>'],
|
|
2020
|
-
|
|
2021
|
-
// replace manually a tags with real ones, converting smart-quotes from google docs
|
|
2022
|
-
[new RegExp(/<a\s+href=("|”|“|“|”)([^&]+)("|”|“|“|”)>/gi), '<a href="$2">']
|
|
2023
|
-
|
|
2024
|
-
];
|
|
2025
|
-
/*jslint regexp: false*/
|
|
2026
|
-
|
|
2027
|
-
for (i = 0; i < replacements.length; i += 1) {
|
|
2028
|
-
text = text.replace(replacements[i][0], replacements[i][1]);
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
if (multiline) {
|
|
2032
|
-
|
|
2033
|
-
// double br's aren't converted to p tags, but we want paragraphs.
|
|
2034
|
-
elList = text.split('<br><br>');
|
|
2035
|
-
|
|
2036
|
-
this.pasteHTML('<p>' + elList.join('</p><p>') + '</p>');
|
|
2037
|
-
this.options.ownerDocument.execCommand('insertText', false, "\n");
|
|
2038
|
-
|
|
2039
|
-
// block element cleanup
|
|
2040
|
-
elList = el.querySelectorAll('a,p,div,br');
|
|
2041
|
-
for (i = 0; i < elList.length; i += 1) {
|
|
2042
|
-
|
|
2043
|
-
workEl = elList[i];
|
|
2044
|
-
|
|
2045
|
-
switch (workEl.tagName.toLowerCase()) {
|
|
2046
|
-
case 'a':
|
|
2047
|
-
if (this.options.targetBlank) {
|
|
2048
|
-
this.setTargetBlank(workEl);
|
|
2049
|
-
}
|
|
2050
|
-
break;
|
|
2051
|
-
case 'p':
|
|
2052
|
-
case 'div':
|
|
2053
|
-
this.filterCommonBlocks(workEl);
|
|
2054
|
-
break;
|
|
2055
|
-
case 'br':
|
|
2056
|
-
this.filterLineBreak(workEl);
|
|
2057
|
-
break;
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
} else {
|
|
2064
|
-
|
|
2065
|
-
this.pasteHTML(text);
|
|
2066
|
-
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2964
|
+
pasteHandler.cleanPaste(text, this.options);
|
|
2069
2965
|
},
|
|
2070
2966
|
|
|
2071
2967
|
pasteHTML: function (html) {
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
pasteBlock.appendChild(this.options.ownerDocument.createElement('body'));
|
|
2075
|
-
|
|
2076
|
-
fragmentBody = pasteBlock.querySelector('body');
|
|
2077
|
-
fragmentBody.innerHTML = html;
|
|
2078
|
-
|
|
2079
|
-
this.cleanupSpans(fragmentBody);
|
|
2080
|
-
|
|
2081
|
-
elList = fragmentBody.querySelectorAll('*');
|
|
2082
|
-
for (i = 0; i < elList.length; i += 1) {
|
|
2083
|
-
|
|
2084
|
-
workEl = elList[i];
|
|
2085
|
-
|
|
2086
|
-
// delete ugly attributes
|
|
2087
|
-
workEl.removeAttribute('class');
|
|
2088
|
-
workEl.removeAttribute('style');
|
|
2089
|
-
workEl.removeAttribute('dir');
|
|
2090
|
-
|
|
2091
|
-
if (workEl.tagName.toLowerCase() === 'meta') {
|
|
2092
|
-
workEl.parentNode.removeChild(workEl);
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
}
|
|
2096
|
-
insertHTMLCommand(this.options.ownerDocument, fragmentBody.innerHTML.replace(/ /g, ' '));
|
|
2097
|
-
},
|
|
2098
|
-
isCommonBlock: function (el) {
|
|
2099
|
-
return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div'));
|
|
2100
|
-
},
|
|
2101
|
-
filterCommonBlocks: function (el) {
|
|
2102
|
-
if (/^\s*$/.test(el.textContent)) {
|
|
2103
|
-
el.parentNode.removeChild(el);
|
|
2104
|
-
}
|
|
2105
|
-
},
|
|
2106
|
-
filterLineBreak: function (el) {
|
|
2107
|
-
if (this.isCommonBlock(el.previousElementSibling)) {
|
|
2108
|
-
|
|
2109
|
-
// remove stray br's following common block elements
|
|
2110
|
-
el.parentNode.removeChild(el);
|
|
2111
|
-
|
|
2112
|
-
} else if (this.isCommonBlock(el.parentNode) && (el.parentNode.firstChild === el || el.parentNode.lastChild === el)) {
|
|
2113
|
-
|
|
2114
|
-
// remove br's just inside open or close tags of a div/p
|
|
2115
|
-
el.parentNode.removeChild(el);
|
|
2116
|
-
|
|
2117
|
-
} else if (el.parentNode.childElementCount === 1) {
|
|
2118
|
-
|
|
2119
|
-
// and br's that are the only child of a div/p
|
|
2120
|
-
this.removeWithParent(el);
|
|
2121
|
-
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
},
|
|
2125
|
-
|
|
2126
|
-
// remove an element, including its parent, if it is the only element within its parent
|
|
2127
|
-
removeWithParent: function (el) {
|
|
2128
|
-
if (el && el.parentNode) {
|
|
2129
|
-
if (el.parentNode.parentNode && el.parentNode.childElementCount === 1) {
|
|
2130
|
-
el.parentNode.parentNode.removeChild(el.parentNode);
|
|
2131
|
-
} else {
|
|
2132
|
-
el.parentNode.removeChild(el.parentNode);
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
},
|
|
2136
|
-
|
|
2137
|
-
cleanupSpans: function (container_el) {
|
|
2138
|
-
|
|
2139
|
-
var i,
|
|
2140
|
-
el,
|
|
2141
|
-
new_el,
|
|
2142
|
-
spans = container_el.querySelectorAll('.replace-with');
|
|
2143
|
-
|
|
2144
|
-
for (i = 0; i < spans.length; i += 1) {
|
|
2145
|
-
|
|
2146
|
-
el = spans[i];
|
|
2147
|
-
new_el = this.options.ownerDocument.createElement(el.classList.contains('bold') ? 'b' : 'i');
|
|
2148
|
-
|
|
2149
|
-
if (el.classList.contains('bold') && el.classList.contains('italic')) {
|
|
2150
|
-
|
|
2151
|
-
// add an i tag as well if this has both italics and bold
|
|
2152
|
-
new_el.innerHTML = '<i>' + el.innerHTML + '</i>';
|
|
2153
|
-
|
|
2154
|
-
} else {
|
|
2155
|
-
|
|
2156
|
-
new_el.innerHTML = el.innerHTML;
|
|
2157
|
-
|
|
2158
|
-
}
|
|
2159
|
-
el.parentNode.replaceChild(new_el, el);
|
|
2160
|
-
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
spans = container_el.querySelectorAll('span');
|
|
2164
|
-
for (i = 0; i < spans.length; i += 1) {
|
|
2165
|
-
|
|
2166
|
-
el = spans[i];
|
|
2167
|
-
|
|
2168
|
-
// remove empty spans, replace others with their contents
|
|
2169
|
-
if (/^\s*$/.test()) {
|
|
2170
|
-
el.parentNode.removeChild(el);
|
|
2171
|
-
} else {
|
|
2172
|
-
el.parentNode.replaceChild(this.options.ownerDocument.createTextNode(el.textContent), el);
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
|
-
}
|
|
2176
|
-
|
|
2968
|
+
pasteHandler.pasteHTML(html, this.options.ownerDocument);
|
|
2177
2969
|
}
|
|
2178
|
-
|
|
2179
2970
|
};
|
|
2180
2971
|
|
|
2181
|
-
}(
|
|
2972
|
+
}());
|
|
2973
|
+
|
|
2974
|
+
return MediumEditor;
|
|
2975
|
+
}()));
|