blocky 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +59 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/blocky/application.js +21 -0
- data/app/assets/javascripts/blocky/flash_messages.js +20 -0
- data/app/assets/stylesheets/blocky/_imports.scss +3 -0
- data/app/assets/stylesheets/blocky/_variables.scss +37 -0
- data/app/assets/stylesheets/blocky/application.css.scss +20 -0
- data/app/assets/stylesheets/blocky/application/content_blocks/edit.scss +13 -0
- data/app/assets/stylesheets/blocky/application/content_blocks/index.scss +81 -0
- data/app/assets/stylesheets/blocky/application/layout.scss +30 -0
- data/app/assets/stylesheets/blocky/shared/buttons.scss +119 -0
- data/app/assets/stylesheets/blocky/shared/flash_messages.scss +28 -0
- data/app/assets/stylesheets/blocky/shared/typography/body.scss +91 -0
- data/app/assets/stylesheets/blocky/shared/typography/forms.scss +81 -0
- data/app/assets/stylesheets/blocky/shared/typography/headings.scss +38 -0
- data/app/assets/stylesheets/blocky/shared/typography/lists.scss +22 -0
- data/app/controllers/blocky/application_controller.rb +7 -0
- data/app/controllers/blocky/content_blocks_controller.rb +44 -0
- data/app/controllers/concerns/blocky/auth.rb +53 -0
- data/app/helpers/blocky/application_helper.rb +26 -0
- data/app/helpers/blocky_helper.rb +18 -0
- data/app/models/blocky/ability.rb +11 -0
- data/app/models/blocky/content_block.rb +27 -0
- data/app/views/blocky/content_blocks/_content_block.html.erb +1 -0
- data/app/views/blocky/content_blocks/edit.html.erb +44 -0
- data/app/views/blocky/content_blocks/index.html.erb +84 -0
- data/app/views/layouts/blocky/_flash_messages.html.erb +13 -0
- data/app/views/layouts/blocky/application.html.erb +20 -0
- data/config/jshint.json +28 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20140326210255_create_blocky_content_blocks.rb +12 -0
- data/lib/blocky.rb +8 -0
- data/lib/blocky/engine.rb +19 -0
- data/lib/blocky/version.rb +3 -0
- data/lib/generators/blocky/install_generator.rb +94 -0
- data/lib/tasks/blocky_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/static_pages_controller.rb +7 -0
- data/spec/dummy/app/helpers/application_helper.rb +3 -0
- data/spec/dummy/app/models/blocky/ability.rb +9 -0
- data/spec/dummy/app/views/layouts/application.html.erb +25 -0
- data/spec/dummy/app/views/static_pages/contact.html.erb +5 -0
- data/spec/dummy/app/views/static_pages/home.html.erb +7 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +23 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/blocky.rb +4 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +12 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140326211453_create_blocky_content_blocks.blocky.rb +13 -0
- data/spec/dummy/db/schema.rb +25 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +14038 -0
- data/spec/dummy/log/test.log +151 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/cache/E76/DE0/blocky%2Fcontent_blocks%2F1-20140326211612204255000 +1 -0
- data/spec/dummy/tmp/cache/E79/630/blocky%2Fcontent_blocks%2F5-20140326220740803013000 +0 -0
- data/spec/dummy/tmp/cache/E7D/2C0/blocky%2Fcontent_blocks%2F2-20140326211803501817000 +1 -0
- data/spec/dummy/tmp/cache/E7F/480/blocky%2Fcontent_blocks%2F2-20140326211735204743000 +0 -0
- data/spec/dummy/tmp/cache/E83/720/blocky%2Fcontent_blocks%2F3-20140326212218805733000 +0 -0
- data/spec/dummy/tmp/cache/E85/4F0/blocky%2Fcontent_blocks%2F1-20140326211503578725000 +0 -0
- data/spec/dummy/tmp/cache/E8A/C50/blocky%2Fcontent_blocks%2F6-20140520152442627674000 +0 -0
- data/spec/dummy/tmp/cache/E8F/EE0/blocky%2Fcontent_blocks%2F4-20140326214706659274000 +0 -0
- data/spec/dummy/tmp/cache/E95/020/blocky%2Fcontent_blocks%2F4-20140520170832796695000 +2 -0
- data/spec/dummy/tmp/cache/assets/development/sass/1330522281c54fc4f0f88c06856f0cc8052c25e8/buttons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/1330522281c54fc4f0f88c06856f0cc8052c25e8/flash_messages.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_compact.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_flex-grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_grid-width.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_linear-gradient.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_modular-scale.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_px-to-em.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_radial-gradient.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_tint-shade.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/16328cd177fa77f37d597cddc166b9d61ed7bdc6/_transition-property-name.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_bordered-pulled.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_core.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_fixed-width.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_icons.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_larger.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_list.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_mixins.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_path.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_rotated-flipped.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_spinning.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_stacked.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/2ed2f1a12f96a5966a3f2f4a226be3068f8e609b/_variables.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_fill-parent.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_media.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_omega.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_outer-container.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_pad.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_private.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_reset.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_row.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_shift.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_span-columns.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_to-deprecate.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/3fb34f29991e8a63348b29fade50f1de29147c8f/_visual-grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/45a8d9925ba36450f7e3862c914588ae8720003e/_imports.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/45a8d9925ba36450f7e3862c914588ae8720003e/_variables.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/45a8d9925ba36450f7e3862c914588ae8720003e/application.css.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_deprecated-webkit-gradient.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_gradient-positions-parser.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_linear-positions-parser.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_radial-arg-parser.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_radial-positions-parser.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_render-gradients.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/4808b00b5a9150b9a854e6877435916328f1da81/_shape-size-stripper.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_animation.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_appearance.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_backface-visibility.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_background-image.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_background.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_border-image.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_border-radius.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_box-sizing.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_columns.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_flex-box.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_font-face.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_hidpi-media-query.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_image-rendering.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_inline-block.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_keyframes.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_linear-gradient.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_perspective.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_placeholder.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_radial-gradient.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_transform.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_transition.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/5e68b12f5742e819b59fae21d1dedfbc3da30721/_user-select.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/7c52080b507114ff73ac654c0177e1dca603143b/body.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/7c52080b507114ff73ac654c0177e1dca603143b/forms.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/7c52080b507114ff73ac654c0177e1dca603143b/headings.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/7c52080b507114ff73ac654c0177e1dca603143b/lists.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/86088d5ecf0a5701fd22ac3b30255eb2bb94f550/_bourbon-deprecated-upcoming.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/9de1036a47599e1de75fdb2094e4960ffb18ffcf/_grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/9de1036a47599e1de75fdb2094e4960ffb18ffcf/_visual-grid.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/9e0f22094073a2727149869ee451bf5155b6b1de/_bourbon.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/9e0f22094073a2727149869ee451bf5155b6b1de/_neat.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/9e0f22094073a2727149869ee451bf5155b6b1de/font-awesome.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/ab9fa2e8e2c4d0592ca80a0885630a538ee36082/_neat-helpers.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/ccd8e9094d6c3af8a9493bb7fee8eae1bf9b4c52/_new-breakpoint.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/ccd8e9094d6c3af8a9493bb7fee8eae1bf9b4c52/_private.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e32eee285e5a7bcd847a61263245c70a38794724/edit.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e32eee285e5a7bcd847a61263245c70a38794724/index.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_button.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_clearfix.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_font-family.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_hide-text.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_html5-input-types.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_position.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_prefixer.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_retina-image.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_size.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_timing-functions.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/e792c58dcbc932d7b0bbb62be67c59da9796d04b/_triangle.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sass/f6aab6f5c59ff0b3c4ff7e11237d10cd45b5353c/layout.scssc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/00b315c99e05fd17eb6ee63c05874610 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0427945c7b4a3a10bc1b628680c1ac11 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/07a7381ab720b1fcdf48d09e6f4aca3f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0cba4b819c78286147e22cce1cff06de +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0d045cc286244754010ae01a766487a9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0f027312d7555aefd043bc94e678e35d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1590fdab706270c5d350582acc774711 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/17d324480cb2a2a47bedc3ef4d022b6d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/21eee9c5050e081a5a1cf91cea7d268f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2510376ff2d7b20d23bc38a46fcfa055 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/26dfb7b51a72e95c414c783f2ba69222 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2a12abb42fb17b6520d9d9ad589a20af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2a87faf509efd55dfdbd16b0fa8239b4 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2c9e3b499f8c7da0a3d3de2510f78846 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2fd51b2bfa391389108d8de69733d84b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3075115024bd10f7adb951b3baec184c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/309a524a7494382fd3346a82b810c20b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/346918a665641c5e32c0bb368f032b07 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/359a931a346dd73a40ea88a3e145e4b3 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3a8c2654e77e18a35f25642e5e3bcb88 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3a8ecba67750b8ad7df3ba9362857756 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3aa302111c6bea14859db6fea34cdb94 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3b05198beb41d8fc2b4a207128140f89 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3cc020c5e2e8ab2e4acd1b5a9fdc16ab +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/3d89aed04c6cdc16f097a2aabfa2b867 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/420c2d9425469c305387fa81d5ef1b30 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/43272d99260411613e550de399f08be8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4443d626de6926b2b0e97011a9d3283b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/44ee5490907c6fdf6530eeea203a3908 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4c016d0efbbfe9fa01bc4d8d800f8548 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4f41b46926dc79b6d378c60e5e12b1ee +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/4ffbbdb75e91e3ef76f0356bd8c9fdb9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/530a0c5b0d469facb66e00ca952d9069 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/562b764135bc28106a524123de319fac +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/59cc9befd7bad256dc0139dc7c32cf34 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5d455edb538f158d67b710d4c8fb9674 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5ea0f006a9c4f5744aa307242e43d11a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5f1b58c3d1b05b422b95fad6ff1236ad +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5f9c0944ded3ac10412c77764c4f83a2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/613e23bb1e9f2c8dbd8166803b85c57c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/61ac9eefcbf748e59c79b6e84835babd +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/64b57c4794688cd1dd9723c89f564ad7 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/65e56fb427fa0e3e148515a9ea998426 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7344269d882a3e910b3806b007dd9d71 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/74104d7c75a8966708478065693bcca8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7415c9f0125deaebddd1c12b2c26367d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7703867f0dd6cf86b9eb01bce45d1f02 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7871e5235770cb3607023e3646149255 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/7ce4b6005f5a1535c297ebe897723551 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8035b3603b1f603513ce0491ef5ee98c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/87b0d0df42a2bb5d08a5417fd0274a6e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/88b8972e085bc284af152cf7b158334c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8b94d81de8f6f238037cab9a4a1a0c30 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8bd0f5b2d316327657d6709cf130a550 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8efc9dce2839c7874c8c1bb8a662acc9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/8f4e3d9674b96cae9b98843cd6ff18c8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/92a576a2bd81929a26b143cc02e430aa +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/9e58e917e624e0cd3b2154f01fd24962 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a0c1f9e3d33f8de75d0a594321456c9b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a147199745f7229dc608d3cefce5df45 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/a72bca95d5c7b54e61fa4e0ebc869d4e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/abe150382a2bf6466264fe8866f34157 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/afa453539adb4bc1f7d844dcb361460d +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b0352a0c3bd6f28326a7fc34fa51fa16 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b0f77fd26cd7fd672f25ec90ed445fe9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b0f88bcdb6579681288ffc314d36281b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b304f5f5248fd1b0910adaeaf4430da9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b5dd7a2c9cdd592440c578b49edff2cb +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b7f85043b18466180fb325cbc7e709c8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b8096cbfaf0aa44715d5411926461117 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b95fb213c4dd9e7f1fdfe6120e88e2e8 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/be95835376c06f09fdd2a3afc3dd195e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/bfacd7859276780db88488d7d7e41b59 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c954e3a10a9a27d22bbde51534852ce1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cee17b1df9168d8c9075ec7c74d54cb5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d101f82ff4e2f7db4eb46cc760aa1d3f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d1689f5fa7ff2ae20077f9de7fd130e1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d31431b0b9739b7fa0843ccfd02d002b +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d410c298186b4110d38ff3d89b29c925 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d4b45f77d1eed50fcf90f8c5511b99b6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d62a46b4d21da7c9091dcbd8fa01da34 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d6477bff4412a5abb06c8acf405c8a87 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d985df74e53b12bfa3af9ea8608eacb2 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d9db27d114516b1b698ad3520e97d1f9 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/da4c7a7a685a765695e1d781590e6a5a +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/dbe056c885afef869a46852b1faa9e60 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/df0a14f77c1e0b39c4284d6d6fce9c97 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e01e232c8b2f8d1711bcd44d6e017dab +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e333f0976af05d7bc7eca78d1383c2af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/e95f94b756f854b191f179bd558ce3c6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/eb20aca7df145f553b39afab67d5c0a0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/edaff551a27cde6faf69cc45ba5de859 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ee44b9ce0a9a1e5e56418ab84da62277 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f345273bfd8088c36038c49a5fae3218 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f3a1691881f9395089d43324197ff863 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f4e80d0621ebd9be1b2b595ce543037f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f521da421cda5fe4162aeec62e594eca +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f6fcfc1f4419ff69a85c86234ba475cf +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f8c2ccce91ab7c89ad8922be93c6f363 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fa0df232aba62e841f6077c735cec1aa +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/fb892eb8050ec923cc8084138b1e6e28 +0 -0
- data/spec/factories/content_block_factory.rb +13 -0
- data/spec/models/blocky/content_block_spec.rb +37 -0
- data/spec/spec_helper.rb +34 -0
- data/vendor/assets/fonts/blocky/FontAwesome.otf +0 -0
- data/vendor/assets/fonts/blocky/fontawesome-webfont.eot +0 -0
- data/vendor/assets/fonts/blocky/fontawesome-webfont.svg +414 -0
- data/vendor/assets/fonts/blocky/fontawesome-webfont.ttf +0 -0
- data/vendor/assets/fonts/blocky/fontawesome-webfont.woff +0 -0
- data/vendor/assets/javascripts/blocky/bootstrap.js +1951 -0
- data/vendor/assets/javascripts/blocky/codemirror/closebrackets.js +84 -0
- data/vendor/assets/javascripts/blocky/codemirror/codemirror.js +4442 -0
- data/vendor/assets/javascripts/blocky/codemirror/formatting.js +1 -0
- data/vendor/assets/javascripts/blocky/codemirror/xml.js +1 -0
- data/vendor/assets/javascripts/blocky/jquery.js +10338 -0
- data/vendor/assets/javascripts/blocky/jquery_ujs.js +398 -0
- data/vendor/assets/javascripts/blocky/summernote.js +3922 -0
- data/vendor/assets/stylesheets/blocky/_bourbon.scss +59 -0
- data/vendor/assets/stylesheets/blocky/_neat.scss +21 -0
- data/vendor/assets/stylesheets/blocky/bootstrap.css +5785 -0
- data/vendor/assets/stylesheets/blocky/bootstrap.css.map +1 -0
- data/vendor/assets/stylesheets/blocky/bourbon/_bourbon-deprecated-upcoming.scss +13 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_button.scss +273 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_clearfix.scss +29 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_font-family.scss +5 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_hide-text.scss +5 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_html5-input-types.scss +56 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_position.scss +42 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_prefixer.scss +49 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_retina-image.scss +32 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_size.scss +44 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_timing-functions.scss +32 -0
- data/vendor/assets/stylesheets/blocky/bourbon/addons/_triangle.scss +45 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_animation.scss +52 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_appearance.scss +3 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_backface-visibility.scss +6 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_background-image.scss +48 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_background.scss +103 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_border-image.scss +55 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_border-radius.scss +22 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_box-sizing.scss +4 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_columns.scss +47 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_flex-box.scss +52 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_font-face.scss +23 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_hidpi-media-query.scss +10 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_image-rendering.scss +13 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_inline-block.scss +8 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_keyframes.scss +43 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_linear-gradient.scss +41 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_perspective.scss +8 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_placeholder.scss +29 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_radial-gradient.scss +44 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_transform.scss +15 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_transition.scss +34 -0
- data/vendor/assets/stylesheets/blocky/bourbon/css3/_user-select.scss +3 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_compact.scss +11 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_flex-grid.scss +39 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_grid-width.scss +13 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_linear-gradient.scss +13 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_modular-scale.scss +40 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_px-to-em.scss +8 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_radial-gradient.scss +23 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_tint-shade.scss +9 -0
- data/vendor/assets/stylesheets/blocky/bourbon/functions/_transition-property-name.scss +22 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_deprecated-webkit-gradient.scss +39 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_gradient-positions-parser.scss +13 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_linear-positions-parser.scss +61 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_radial-arg-parser.scss +69 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_radial-positions-parser.scss +18 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_render-gradients.scss +26 -0
- data/vendor/assets/stylesheets/blocky/bourbon/helpers/_shape-size-stripper.scss +10 -0
- data/vendor/assets/stylesheets/blocky/codemirror/codemirror.css +1 -0
- data/vendor/assets/stylesheets/blocky/codemirror/monokai.css +1 -0
- data/vendor/assets/stylesheets/blocky/font-awesome.scss +17 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_bordered-pulled.scss +16 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_core.scss +12 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_fixed-width.scss +6 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_icons.scss +412 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_larger.scss +13 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_list.scss +19 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_mixins.scss +20 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_path.scss +13 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_rotated-flipped.scss +9 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_spinning.scss +30 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_stacked.scss +20 -0
- data/vendor/assets/stylesheets/blocky/font-awesome/_variables.scss +381 -0
- data/vendor/assets/stylesheets/blocky/neat/_neat-helpers.scss +7 -0
- data/vendor/assets/stylesheets/blocky/neat/functions/_new-breakpoint.scss +16 -0
- data/vendor/assets/stylesheets/blocky/neat/functions/_private.scss +125 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_fill-parent.scss +7 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_grid.scss +5 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_media.scss +51 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_omega.scss +79 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_outer-container.scss +8 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_pad.scss +8 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_private.scss +50 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_reset.scss +12 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_row.scss +17 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_shift.scss +16 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_span-columns.scss +45 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_to-deprecate.scss +57 -0
- data/vendor/assets/stylesheets/blocky/neat/grid/_visual-grid.scss +41 -0
- data/vendor/assets/stylesheets/blocky/neat/settings/_grid.scss +7 -0
- data/vendor/assets/stylesheets/blocky/neat/settings/_visual-grid.scss +5 -0
- data/vendor/assets/stylesheets/blocky/normalize.css +423 -0
- data/vendor/assets/stylesheets/blocky/summernote.css +1 -0
- metadata +991 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
(function($, undefined) {
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unobtrusive scripting adapter for jQuery
|
|
5
|
+
* https://github.com/rails/jquery-ujs
|
|
6
|
+
*
|
|
7
|
+
* Requires jQuery 1.7.0 or later.
|
|
8
|
+
*
|
|
9
|
+
* Released under the MIT license
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Cut down on the number of issues from people inadvertently including jquery_ujs twice
|
|
14
|
+
// by detecting and raising an error when it happens.
|
|
15
|
+
if ( $.rails !== undefined ) {
|
|
16
|
+
$.error('jquery-ujs has already been loaded!');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Shorthand to make it a little easier to call public rails functions from within rails.js
|
|
20
|
+
var rails;
|
|
21
|
+
var $document = $(document);
|
|
22
|
+
|
|
23
|
+
$.rails = rails = {
|
|
24
|
+
// Link elements bound by jquery-ujs
|
|
25
|
+
linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
|
|
26
|
+
|
|
27
|
+
// Button elements bound by jquery-ujs
|
|
28
|
+
buttonClickSelector: 'button[data-remote]',
|
|
29
|
+
|
|
30
|
+
// Select elements bound by jquery-ujs
|
|
31
|
+
inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
|
|
32
|
+
|
|
33
|
+
// Form elements bound by jquery-ujs
|
|
34
|
+
formSubmitSelector: 'form',
|
|
35
|
+
|
|
36
|
+
// Form input elements bound by jquery-ujs
|
|
37
|
+
formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
|
|
38
|
+
|
|
39
|
+
// Form input elements disabled during form submission
|
|
40
|
+
disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
|
|
41
|
+
|
|
42
|
+
// Form input elements re-enabled after form submission
|
|
43
|
+
enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
|
|
44
|
+
|
|
45
|
+
// Form required input elements
|
|
46
|
+
requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
|
|
47
|
+
|
|
48
|
+
// Form file input elements
|
|
49
|
+
fileInputSelector: 'input[type=file]',
|
|
50
|
+
|
|
51
|
+
// Link onClick disable selector with possible reenable after remote submission
|
|
52
|
+
linkDisableSelector: 'a[data-disable-with]',
|
|
53
|
+
|
|
54
|
+
// Make sure that every Ajax request sends the CSRF token
|
|
55
|
+
CSRFProtection: function(xhr) {
|
|
56
|
+
var token = $('meta[name="csrf-token"]').attr('content');
|
|
57
|
+
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// making sure that all forms have actual up-to-date token(cached forms contain old one)
|
|
61
|
+
refreshCSRFTokens: function(){
|
|
62
|
+
var csrfToken = $('meta[name=csrf-token]').attr('content');
|
|
63
|
+
var csrfParam = $('meta[name=csrf-param]').attr('content');
|
|
64
|
+
$('form input[name="' + csrfParam + '"]').val(csrfToken);
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// Triggers an event on an element and returns false if the event result is false
|
|
68
|
+
fire: function(obj, name, data) {
|
|
69
|
+
var event = $.Event(name);
|
|
70
|
+
obj.trigger(event, data);
|
|
71
|
+
return event.result !== false;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
|
|
75
|
+
confirm: function(message) {
|
|
76
|
+
return confirm(message);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Default ajax function, may be overridden with custom function in $.rails.ajax
|
|
80
|
+
ajax: function(options) {
|
|
81
|
+
return $.ajax(options);
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Default way to get an element's href. May be overridden at $.rails.href.
|
|
85
|
+
href: function(element) {
|
|
86
|
+
return element.attr('href');
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// Submits "remote" forms and links with ajax
|
|
90
|
+
handleRemote: function(element) {
|
|
91
|
+
var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
|
|
92
|
+
|
|
93
|
+
if (rails.fire(element, 'ajax:before')) {
|
|
94
|
+
elCrossDomain = element.data('cross-domain');
|
|
95
|
+
crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
|
|
96
|
+
withCredentials = element.data('with-credentials') || null;
|
|
97
|
+
dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
|
|
98
|
+
|
|
99
|
+
if (element.is('form')) {
|
|
100
|
+
method = element.attr('method');
|
|
101
|
+
url = element.attr('action');
|
|
102
|
+
data = element.serializeArray();
|
|
103
|
+
// memoized value from clicked submit button
|
|
104
|
+
var button = element.data('ujs:submit-button');
|
|
105
|
+
if (button) {
|
|
106
|
+
data.push(button);
|
|
107
|
+
element.data('ujs:submit-button', null);
|
|
108
|
+
}
|
|
109
|
+
} else if (element.is(rails.inputChangeSelector)) {
|
|
110
|
+
method = element.data('method');
|
|
111
|
+
url = element.data('url');
|
|
112
|
+
data = element.serialize();
|
|
113
|
+
if (element.data('params')) data = data + "&" + element.data('params');
|
|
114
|
+
} else if (element.is(rails.buttonClickSelector)) {
|
|
115
|
+
method = element.data('method') || 'get';
|
|
116
|
+
url = element.data('url');
|
|
117
|
+
data = element.serialize();
|
|
118
|
+
if (element.data('params')) data = data + "&" + element.data('params');
|
|
119
|
+
} else {
|
|
120
|
+
method = element.data('method');
|
|
121
|
+
url = rails.href(element);
|
|
122
|
+
data = element.data('params') || null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
options = {
|
|
126
|
+
type: method || 'GET', data: data, dataType: dataType,
|
|
127
|
+
// stopping the "ajax:beforeSend" event will cancel the ajax request
|
|
128
|
+
beforeSend: function(xhr, settings) {
|
|
129
|
+
if (settings.dataType === undefined) {
|
|
130
|
+
xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
|
|
131
|
+
}
|
|
132
|
+
return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
|
|
133
|
+
},
|
|
134
|
+
success: function(data, status, xhr) {
|
|
135
|
+
element.trigger('ajax:success', [data, status, xhr]);
|
|
136
|
+
},
|
|
137
|
+
complete: function(xhr, status) {
|
|
138
|
+
element.trigger('ajax:complete', [xhr, status]);
|
|
139
|
+
},
|
|
140
|
+
error: function(xhr, status, error) {
|
|
141
|
+
element.trigger('ajax:error', [xhr, status, error]);
|
|
142
|
+
},
|
|
143
|
+
crossDomain: crossDomain
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// There is no withCredentials for IE6-8 when
|
|
147
|
+
// "Enable native XMLHTTP support" is disabled
|
|
148
|
+
if (withCredentials) {
|
|
149
|
+
options.xhrFields = {
|
|
150
|
+
withCredentials: withCredentials
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Only pass url to `ajax` options if not blank
|
|
155
|
+
if (url) { options.url = url; }
|
|
156
|
+
|
|
157
|
+
var jqxhr = rails.ajax(options);
|
|
158
|
+
element.trigger('ajax:send', jqxhr);
|
|
159
|
+
return jqxhr;
|
|
160
|
+
} else {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Handles "data-method" on links such as:
|
|
166
|
+
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
|
|
167
|
+
handleMethod: function(link) {
|
|
168
|
+
var href = rails.href(link),
|
|
169
|
+
method = link.data('method'),
|
|
170
|
+
target = link.attr('target'),
|
|
171
|
+
csrfToken = $('meta[name=csrf-token]').attr('content'),
|
|
172
|
+
csrfParam = $('meta[name=csrf-param]').attr('content'),
|
|
173
|
+
form = $('<form method="post" action="' + href + '"></form>'),
|
|
174
|
+
metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
|
|
175
|
+
|
|
176
|
+
if (csrfParam !== undefined && csrfToken !== undefined) {
|
|
177
|
+
metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (target) { form.attr('target', target); }
|
|
181
|
+
|
|
182
|
+
form.hide().append(metadataInput).appendTo('body');
|
|
183
|
+
form.submit();
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
/* Disables form elements:
|
|
187
|
+
- Caches element value in 'ujs:enable-with' data store
|
|
188
|
+
- Replaces element text with value of 'data-disable-with' attribute
|
|
189
|
+
- Sets disabled property to true
|
|
190
|
+
*/
|
|
191
|
+
disableFormElements: function(form) {
|
|
192
|
+
form.find(rails.disableSelector).each(function() {
|
|
193
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
|
194
|
+
element.data('ujs:enable-with', element[method]());
|
|
195
|
+
element[method](element.data('disable-with'));
|
|
196
|
+
element.prop('disabled', true);
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/* Re-enables disabled form elements:
|
|
201
|
+
- Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
|
|
202
|
+
- Sets disabled property to false
|
|
203
|
+
*/
|
|
204
|
+
enableFormElements: function(form) {
|
|
205
|
+
form.find(rails.enableSelector).each(function() {
|
|
206
|
+
var element = $(this), method = element.is('button') ? 'html' : 'val';
|
|
207
|
+
if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
|
|
208
|
+
element.prop('disabled', false);
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/* For 'data-confirm' attribute:
|
|
213
|
+
- Fires `confirm` event
|
|
214
|
+
- Shows the confirmation dialog
|
|
215
|
+
- Fires the `confirm:complete` event
|
|
216
|
+
|
|
217
|
+
Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
|
|
218
|
+
Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
|
|
219
|
+
Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
|
|
220
|
+
return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
|
|
221
|
+
*/
|
|
222
|
+
allowAction: function(element) {
|
|
223
|
+
var message = element.data('confirm'),
|
|
224
|
+
answer = false, callback;
|
|
225
|
+
if (!message) { return true; }
|
|
226
|
+
|
|
227
|
+
if (rails.fire(element, 'confirm')) {
|
|
228
|
+
answer = rails.confirm(message);
|
|
229
|
+
callback = rails.fire(element, 'confirm:complete', [answer]);
|
|
230
|
+
}
|
|
231
|
+
return answer && callback;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// Helper function which checks for blank inputs in a form that match the specified CSS selector
|
|
235
|
+
blankInputs: function(form, specifiedSelector, nonBlank) {
|
|
236
|
+
var inputs = $(), input, valueToCheck,
|
|
237
|
+
selector = specifiedSelector || 'input,textarea',
|
|
238
|
+
allInputs = form.find(selector);
|
|
239
|
+
|
|
240
|
+
allInputs.each(function() {
|
|
241
|
+
input = $(this);
|
|
242
|
+
valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
|
|
243
|
+
// If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
|
|
244
|
+
if (!valueToCheck === !nonBlank) {
|
|
245
|
+
|
|
246
|
+
// Don't count unchecked required radio if other radio with same name is checked
|
|
247
|
+
if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
|
|
248
|
+
return true; // Skip to next input
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
inputs = inputs.add(input);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
return inputs.length ? inputs : false;
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// Helper function which checks for non-blank inputs in a form that match the specified CSS selector
|
|
258
|
+
nonBlankInputs: function(form, specifiedSelector) {
|
|
259
|
+
return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// Helper function, needed to provide consistent behavior in IE
|
|
263
|
+
stopEverything: function(e) {
|
|
264
|
+
$(e.target).trigger('ujs:everythingStopped');
|
|
265
|
+
e.stopImmediatePropagation();
|
|
266
|
+
return false;
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// replace element's html with the 'data-disable-with' after storing original html
|
|
270
|
+
// and prevent clicking on it
|
|
271
|
+
disableElement: function(element) {
|
|
272
|
+
element.data('ujs:enable-with', element.html()); // store enabled state
|
|
273
|
+
element.html(element.data('disable-with')); // set to disabled state
|
|
274
|
+
element.bind('click.railsDisable', function(e) { // prevent further clicking
|
|
275
|
+
return rails.stopEverything(e);
|
|
276
|
+
});
|
|
277
|
+
},
|
|
278
|
+
|
|
279
|
+
// restore element to its original state which was disabled by 'disableElement' above
|
|
280
|
+
enableElement: function(element) {
|
|
281
|
+
if (element.data('ujs:enable-with') !== undefined) {
|
|
282
|
+
element.html(element.data('ujs:enable-with')); // set to old enabled state
|
|
283
|
+
element.removeData('ujs:enable-with'); // clean up cache
|
|
284
|
+
}
|
|
285
|
+
element.unbind('click.railsDisable'); // enable element
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
if (rails.fire($document, 'rails:attachBindings')) {
|
|
291
|
+
|
|
292
|
+
$.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
|
|
293
|
+
|
|
294
|
+
$document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
|
|
295
|
+
rails.enableElement($(this));
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
$document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
|
|
299
|
+
var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
|
|
300
|
+
if (!rails.allowAction(link)) return rails.stopEverything(e);
|
|
301
|
+
|
|
302
|
+
if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
|
|
303
|
+
|
|
304
|
+
if (link.data('remote') !== undefined) {
|
|
305
|
+
if (metaClick && (!method || method === 'GET') && !data) { return true; }
|
|
306
|
+
|
|
307
|
+
var handleRemote = rails.handleRemote(link);
|
|
308
|
+
// response from rails.handleRemote() will either be false or a deferred object promise.
|
|
309
|
+
if (handleRemote === false) {
|
|
310
|
+
rails.enableElement(link);
|
|
311
|
+
} else {
|
|
312
|
+
handleRemote.error( function() { rails.enableElement(link); } );
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
|
|
316
|
+
} else if (link.data('method')) {
|
|
317
|
+
rails.handleMethod(link);
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
$document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
|
|
323
|
+
var button = $(this);
|
|
324
|
+
if (!rails.allowAction(button)) return rails.stopEverything(e);
|
|
325
|
+
|
|
326
|
+
rails.handleRemote(button);
|
|
327
|
+
return false;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
$document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
|
|
331
|
+
var link = $(this);
|
|
332
|
+
if (!rails.allowAction(link)) return rails.stopEverything(e);
|
|
333
|
+
|
|
334
|
+
rails.handleRemote(link);
|
|
335
|
+
return false;
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
$document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
|
|
339
|
+
var form = $(this),
|
|
340
|
+
remote = form.data('remote') !== undefined,
|
|
341
|
+
blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
|
|
342
|
+
nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
|
|
343
|
+
|
|
344
|
+
if (!rails.allowAction(form)) return rails.stopEverything(e);
|
|
345
|
+
|
|
346
|
+
// skip other logic when required values are missing or file upload is present
|
|
347
|
+
if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
|
|
348
|
+
return rails.stopEverything(e);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (remote) {
|
|
352
|
+
if (nonBlankFileInputs) {
|
|
353
|
+
// slight timeout so that the submit button gets properly serialized
|
|
354
|
+
// (make it easy for event handler to serialize form without disabled values)
|
|
355
|
+
setTimeout(function(){ rails.disableFormElements(form); }, 13);
|
|
356
|
+
var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
|
|
357
|
+
|
|
358
|
+
// re-enable form elements if event bindings return false (canceling normal form submission)
|
|
359
|
+
if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
|
|
360
|
+
|
|
361
|
+
return aborted;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
rails.handleRemote(form);
|
|
365
|
+
return false;
|
|
366
|
+
|
|
367
|
+
} else {
|
|
368
|
+
// slight timeout so that the submit button gets properly serialized
|
|
369
|
+
setTimeout(function(){ rails.disableFormElements(form); }, 13);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
$document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
|
|
374
|
+
var button = $(this);
|
|
375
|
+
|
|
376
|
+
if (!rails.allowAction(button)) return rails.stopEverything(event);
|
|
377
|
+
|
|
378
|
+
// register the pressed submit button
|
|
379
|
+
var name = button.attr('name'),
|
|
380
|
+
data = name ? {name:name, value:button.val()} : null;
|
|
381
|
+
|
|
382
|
+
button.closest('form').data('ujs:submit-button', data);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
$document.delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) {
|
|
386
|
+
if (this == event.target) rails.disableFormElements($(this));
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
$document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
|
|
390
|
+
if (this == event.target) rails.enableFormElements($(this));
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
$(function(){
|
|
394
|
+
rails.refreshCSRFTokens();
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
})( jQuery );
|
|
@@ -0,0 +1,3922 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Super simple wysiwyg editor on Bootstrap v0.5.2
|
|
3
|
+
* http://hackerwins.github.io/summernote/
|
|
4
|
+
*
|
|
5
|
+
* summernote.js
|
|
6
|
+
* Copyright 2013 Alan Hong. and outher contributors
|
|
7
|
+
* summernote may be freely distributed under the MIT license./
|
|
8
|
+
*
|
|
9
|
+
* Date: 2014-05-16T11:28Z
|
|
10
|
+
*/
|
|
11
|
+
(function (factory) {
|
|
12
|
+
/* global define */
|
|
13
|
+
if (typeof define === 'function' && define.amd) {
|
|
14
|
+
// AMD. Register as an anonymous module.
|
|
15
|
+
define(['jquery', 'codemirror'], factory);
|
|
16
|
+
} else {
|
|
17
|
+
// Browser globals: jQuery, CodeMirror
|
|
18
|
+
factory(window.jQuery, window.CodeMirror);
|
|
19
|
+
}
|
|
20
|
+
}(function ($, CodeMirror) {
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if ('function' !== typeof Array.prototype.reduce) {
|
|
25
|
+
/**
|
|
26
|
+
* Array.prototype.reduce fallback
|
|
27
|
+
*
|
|
28
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
|
|
29
|
+
*/
|
|
30
|
+
Array.prototype.reduce = function (callback, optInitialValue) {
|
|
31
|
+
var idx, value, length = this.length >>> 0, isValueSet = false;
|
|
32
|
+
if (1 < arguments.length) {
|
|
33
|
+
value = optInitialValue;
|
|
34
|
+
isValueSet = true;
|
|
35
|
+
}
|
|
36
|
+
for (idx = 0; length > idx; ++idx) {
|
|
37
|
+
if (this.hasOwnProperty(idx)) {
|
|
38
|
+
if (isValueSet) {
|
|
39
|
+
value = callback(value, this[idx], idx, this);
|
|
40
|
+
} else {
|
|
41
|
+
value = this[idx];
|
|
42
|
+
isValueSet = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!isValueSet) {
|
|
47
|
+
throw new TypeError('Reduce of empty array with no initial value');
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Object which check platform and agent
|
|
55
|
+
*/
|
|
56
|
+
var agent = {
|
|
57
|
+
bMac: navigator.appVersion.indexOf('Mac') > -1,
|
|
58
|
+
bMSIE: navigator.userAgent.indexOf('MSIE') > -1 || navigator.userAgent.indexOf('Trident') > -1,
|
|
59
|
+
bFF: navigator.userAgent.indexOf('Firefox') > -1,
|
|
60
|
+
jqueryVersion: parseFloat($.fn.jquery),
|
|
61
|
+
bCodeMirror: !!CodeMirror
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* func utils (for high-order func's arg)
|
|
66
|
+
*/
|
|
67
|
+
var func = (function () {
|
|
68
|
+
var eq = function (elA) {
|
|
69
|
+
return function (elB) {
|
|
70
|
+
return elA === elB;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
var eq2 = function (elA, elB) {
|
|
75
|
+
return elA === elB;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
var ok = function () {
|
|
79
|
+
return true;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
var fail = function () {
|
|
83
|
+
return false;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
var not = function (f) {
|
|
87
|
+
return function () {
|
|
88
|
+
return !f.apply(f, arguments);
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
var self = function (a) {
|
|
93
|
+
return a;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
var idCounter = 0;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* generate a globally-unique id
|
|
100
|
+
*
|
|
101
|
+
* @param {String} [prefix]
|
|
102
|
+
*/
|
|
103
|
+
var uniqueId = function (prefix) {
|
|
104
|
+
var id = ++idCounter + '';
|
|
105
|
+
return prefix ? prefix + id : id;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* returns bnd (bounds) from rect
|
|
110
|
+
*
|
|
111
|
+
* - IE Compatability Issue: http://goo.gl/sRLOAo
|
|
112
|
+
* - Scroll Issue: http://goo.gl/sNjUc
|
|
113
|
+
*
|
|
114
|
+
* @param {Rect} rect
|
|
115
|
+
* @return {Object} bounds
|
|
116
|
+
* @return {Number} bounds.top
|
|
117
|
+
* @return {Number} bounds.left
|
|
118
|
+
* @return {Number} bounds.width
|
|
119
|
+
* @return {Number} bounds.height
|
|
120
|
+
*/
|
|
121
|
+
var rect2bnd = function (rect) {
|
|
122
|
+
var $document = $(document);
|
|
123
|
+
return {
|
|
124
|
+
top: rect.top + $document.scrollTop(),
|
|
125
|
+
left: rect.left + $document.scrollLeft(),
|
|
126
|
+
width: rect.right - rect.left,
|
|
127
|
+
height: rect.bottom - rect.top
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* returns a copy of the object where the keys have become the values and the values the keys.
|
|
133
|
+
* @param {Object} obj
|
|
134
|
+
* @return {Object}
|
|
135
|
+
*/
|
|
136
|
+
var invertObject = function (obj) {
|
|
137
|
+
var inverted = {};
|
|
138
|
+
for (var key in obj) {
|
|
139
|
+
if (obj.hasOwnProperty(key)) {
|
|
140
|
+
inverted[obj[key]] = key;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return inverted;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
eq: eq,
|
|
148
|
+
eq2: eq2,
|
|
149
|
+
ok: ok,
|
|
150
|
+
fail: fail,
|
|
151
|
+
not: not,
|
|
152
|
+
self: self,
|
|
153
|
+
uniqueId: uniqueId,
|
|
154
|
+
rect2bnd: rect2bnd,
|
|
155
|
+
invertObject: invertObject
|
|
156
|
+
};
|
|
157
|
+
})();
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* list utils
|
|
161
|
+
*/
|
|
162
|
+
var list = (function () {
|
|
163
|
+
/**
|
|
164
|
+
* returns the first element of an array.
|
|
165
|
+
* @param {Array} array
|
|
166
|
+
*/
|
|
167
|
+
var head = function (array) {
|
|
168
|
+
return array[0];
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* returns the last element of an array.
|
|
173
|
+
* @param {Array} array
|
|
174
|
+
*/
|
|
175
|
+
var last = function (array) {
|
|
176
|
+
return array[array.length - 1];
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* returns everything but the last entry of the array.
|
|
181
|
+
* @param {Array} array
|
|
182
|
+
*/
|
|
183
|
+
var initial = function (array) {
|
|
184
|
+
return array.slice(0, array.length - 1);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* returns the rest of the elements in an array.
|
|
189
|
+
* @param {Array} array
|
|
190
|
+
*/
|
|
191
|
+
var tail = function (array) {
|
|
192
|
+
return array.slice(1);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* returns next item.
|
|
197
|
+
* @param {Array} array
|
|
198
|
+
*/
|
|
199
|
+
var next = function (array, item) {
|
|
200
|
+
var idx = array.indexOf(item);
|
|
201
|
+
if (idx === -1) { return null; }
|
|
202
|
+
|
|
203
|
+
return array[idx + 1];
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* returns prev item.
|
|
208
|
+
* @param {Array} array
|
|
209
|
+
*/
|
|
210
|
+
var prev = function (array, item) {
|
|
211
|
+
var idx = array.indexOf(item);
|
|
212
|
+
if (idx === -1) { return null; }
|
|
213
|
+
|
|
214
|
+
return array[idx - 1];
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* get sum from a list
|
|
219
|
+
* @param {Array} array - array
|
|
220
|
+
* @param {Function} fn - iterator
|
|
221
|
+
*/
|
|
222
|
+
var sum = function (array, fn) {
|
|
223
|
+
fn = fn || func.self;
|
|
224
|
+
return array.reduce(function (memo, v) {
|
|
225
|
+
return memo + fn(v);
|
|
226
|
+
}, 0);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* returns a copy of the collection with array type.
|
|
231
|
+
* @param {Collection} collection - collection eg) node.childNodes, ...
|
|
232
|
+
*/
|
|
233
|
+
var from = function (collection) {
|
|
234
|
+
var result = [], idx = -1, length = collection.length;
|
|
235
|
+
while (++idx < length) {
|
|
236
|
+
result[idx] = collection[idx];
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* cluster elements by predicate function.
|
|
243
|
+
* @param {Array} array - array
|
|
244
|
+
* @param {Function} fn - predicate function for cluster rule
|
|
245
|
+
* @param {Array[]}
|
|
246
|
+
*/
|
|
247
|
+
var clusterBy = function (array, fn) {
|
|
248
|
+
if (array.length === 0) { return []; }
|
|
249
|
+
var aTail = tail(array);
|
|
250
|
+
return aTail.reduce(function (memo, v) {
|
|
251
|
+
var aLast = last(memo);
|
|
252
|
+
if (fn(last(aLast), v)) {
|
|
253
|
+
aLast[aLast.length] = v;
|
|
254
|
+
} else {
|
|
255
|
+
memo[memo.length] = [v];
|
|
256
|
+
}
|
|
257
|
+
return memo;
|
|
258
|
+
}, [[head(array)]]);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* returns a copy of the array with all falsy values removed
|
|
263
|
+
* @param {Array} array - array
|
|
264
|
+
* @param {Function} fn - predicate function for cluster rule
|
|
265
|
+
*/
|
|
266
|
+
var compact = function (array) {
|
|
267
|
+
var aResult = [];
|
|
268
|
+
for (var idx = 0, sz = array.length; idx < sz; idx ++) {
|
|
269
|
+
if (array[idx]) { aResult.push(array[idx]); }
|
|
270
|
+
}
|
|
271
|
+
return aResult;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
return { head: head, last: last, initial: initial, tail: tail,
|
|
275
|
+
prev: prev, next: next, sum: sum, from: from,
|
|
276
|
+
compact: compact, clusterBy: clusterBy };
|
|
277
|
+
})();
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Dom functions
|
|
281
|
+
*/
|
|
282
|
+
var dom = (function () {
|
|
283
|
+
/**
|
|
284
|
+
* returns whether node is `note-editable` or not.
|
|
285
|
+
*
|
|
286
|
+
* @param {Element} node
|
|
287
|
+
* @return {Boolean}
|
|
288
|
+
*/
|
|
289
|
+
var isEditable = function (node) {
|
|
290
|
+
return node && $(node).hasClass('note-editable');
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
var isControlSizing = function (node) {
|
|
294
|
+
return node && $(node).hasClass('note-control-sizing');
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* build layoutInfo from $editor(.note-editor)
|
|
299
|
+
*
|
|
300
|
+
* @param {jQuery} $editor
|
|
301
|
+
* @return {Object}
|
|
302
|
+
*/
|
|
303
|
+
var buildLayoutInfo = function ($editor) {
|
|
304
|
+
var makeFinder;
|
|
305
|
+
|
|
306
|
+
// air mode
|
|
307
|
+
if ($editor.hasClass('note-air-editor')) {
|
|
308
|
+
var id = list.last($editor.attr('id').split('-'));
|
|
309
|
+
makeFinder = function (sIdPrefix) {
|
|
310
|
+
return function () { return $(sIdPrefix + id); };
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
editor: function () { return $editor; },
|
|
315
|
+
editable: function () { return $editor; },
|
|
316
|
+
popover: makeFinder('#note-popover-'),
|
|
317
|
+
handle: makeFinder('#note-handle-'),
|
|
318
|
+
dialog: makeFinder('#note-dialog-')
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// frame mode
|
|
322
|
+
} else {
|
|
323
|
+
makeFinder = function (sClassName) {
|
|
324
|
+
return function () { return $editor.find(sClassName); };
|
|
325
|
+
};
|
|
326
|
+
return {
|
|
327
|
+
editor: function () { return $editor; },
|
|
328
|
+
dropzone: makeFinder('.note-dropzone'),
|
|
329
|
+
toolbar: makeFinder('.note-toolbar'),
|
|
330
|
+
editable: makeFinder('.note-editable'),
|
|
331
|
+
codable: makeFinder('.note-codable'),
|
|
332
|
+
statusbar: makeFinder('.note-statusbar'),
|
|
333
|
+
popover: makeFinder('.note-popover'),
|
|
334
|
+
handle: makeFinder('.note-handle'),
|
|
335
|
+
dialog: makeFinder('.note-dialog')
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* returns predicate which judge whether nodeName is same
|
|
342
|
+
* @param {String} sNodeName
|
|
343
|
+
*/
|
|
344
|
+
var makePredByNodeName = function (sNodeName) {
|
|
345
|
+
// nodeName is always uppercase.
|
|
346
|
+
return function (node) {
|
|
347
|
+
return node && node.nodeName === sNodeName;
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
var isPara = function (node) {
|
|
352
|
+
// Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
|
|
353
|
+
return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName);
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
var isList = function (node) {
|
|
357
|
+
return node && /^UL|^OL/.test(node.nodeName);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
var isCell = function (node) {
|
|
361
|
+
return node && /^TD|^TH/.test(node.nodeName);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* find nearest ancestor predicate hit
|
|
366
|
+
*
|
|
367
|
+
* @param {Element} node
|
|
368
|
+
* @param {Function} pred - predicate function
|
|
369
|
+
*/
|
|
370
|
+
var ancestor = function (node, pred) {
|
|
371
|
+
while (node) {
|
|
372
|
+
if (pred(node)) { return node; }
|
|
373
|
+
if (isEditable(node)) { break; }
|
|
374
|
+
|
|
375
|
+
node = node.parentNode;
|
|
376
|
+
}
|
|
377
|
+
return null;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* returns new array of ancestor nodes (until predicate hit).
|
|
382
|
+
*
|
|
383
|
+
* @param {Element} node
|
|
384
|
+
* @param {Function} [optional] pred - predicate function
|
|
385
|
+
*/
|
|
386
|
+
var listAncestor = function (node, pred) {
|
|
387
|
+
pred = pred || func.fail;
|
|
388
|
+
|
|
389
|
+
var aAncestor = [];
|
|
390
|
+
ancestor(node, function (el) {
|
|
391
|
+
aAncestor.push(el);
|
|
392
|
+
return pred(el);
|
|
393
|
+
});
|
|
394
|
+
return aAncestor;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* returns common ancestor node between two nodes.
|
|
399
|
+
*
|
|
400
|
+
* @param {Element} nodeA
|
|
401
|
+
* @param {Element} nodeB
|
|
402
|
+
*/
|
|
403
|
+
var commonAncestor = function (nodeA, nodeB) {
|
|
404
|
+
var aAncestor = listAncestor(nodeA);
|
|
405
|
+
for (var n = nodeB; n; n = n.parentNode) {
|
|
406
|
+
if ($.inArray(n, aAncestor) > -1) { return n; }
|
|
407
|
+
}
|
|
408
|
+
return null; // difference document area
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* listing all Nodes between two nodes.
|
|
413
|
+
* FIXME: nodeA and nodeB must be sorted, use comparePoints later.
|
|
414
|
+
*
|
|
415
|
+
* @param {Element} nodeA
|
|
416
|
+
* @param {Element} nodeB
|
|
417
|
+
*/
|
|
418
|
+
var listBetween = function (nodeA, nodeB) {
|
|
419
|
+
var aNode = [];
|
|
420
|
+
|
|
421
|
+
var bStart = false, bEnd = false;
|
|
422
|
+
|
|
423
|
+
// DFS(depth first search) with commonAcestor.
|
|
424
|
+
(function fnWalk(node) {
|
|
425
|
+
if (!node) { return; } // traverse fisnish
|
|
426
|
+
if (node === nodeA) { bStart = true; } // start point
|
|
427
|
+
if (bStart && !bEnd) { aNode.push(node); } // between
|
|
428
|
+
if (node === nodeB) { bEnd = true; return; } // end point
|
|
429
|
+
|
|
430
|
+
for (var idx = 0, sz = node.childNodes.length; idx < sz; idx++) {
|
|
431
|
+
fnWalk(node.childNodes[idx]);
|
|
432
|
+
}
|
|
433
|
+
})(commonAncestor(nodeA, nodeB));
|
|
434
|
+
|
|
435
|
+
return aNode;
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* listing all previous siblings (until predicate hit).
|
|
440
|
+
* @param {Element} node
|
|
441
|
+
* @param {Function} [optional] pred - predicate function
|
|
442
|
+
*/
|
|
443
|
+
var listPrev = function (node, pred) {
|
|
444
|
+
pred = pred || func.fail;
|
|
445
|
+
|
|
446
|
+
var aNext = [];
|
|
447
|
+
while (node) {
|
|
448
|
+
aNext.push(node);
|
|
449
|
+
if (pred(node)) { break; }
|
|
450
|
+
node = node.previousSibling;
|
|
451
|
+
}
|
|
452
|
+
return aNext;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* listing next siblings (until predicate hit).
|
|
457
|
+
*
|
|
458
|
+
* @param {Element} node
|
|
459
|
+
* @param {Function} [pred] - predicate function
|
|
460
|
+
*/
|
|
461
|
+
var listNext = function (node, pred) {
|
|
462
|
+
pred = pred || func.fail;
|
|
463
|
+
|
|
464
|
+
var aNext = [];
|
|
465
|
+
while (node) {
|
|
466
|
+
aNext.push(node);
|
|
467
|
+
if (pred(node)) { break; }
|
|
468
|
+
node = node.nextSibling;
|
|
469
|
+
}
|
|
470
|
+
return aNext;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* listing descendant nodes
|
|
475
|
+
*
|
|
476
|
+
* @param {Element} node
|
|
477
|
+
* @param {Function} [pred] - predicate function
|
|
478
|
+
*/
|
|
479
|
+
var listDescendant = function (node, pred) {
|
|
480
|
+
var aDescendant = [];
|
|
481
|
+
pred = pred || func.ok;
|
|
482
|
+
|
|
483
|
+
// start DFS(depth first search) with node
|
|
484
|
+
(function fnWalk(current) {
|
|
485
|
+
if (node !== current && pred(current)) {
|
|
486
|
+
aDescendant.push(current);
|
|
487
|
+
}
|
|
488
|
+
for (var idx = 0, sz = current.childNodes.length; idx < sz; idx++) {
|
|
489
|
+
fnWalk(current.childNodes[idx]);
|
|
490
|
+
}
|
|
491
|
+
})(node);
|
|
492
|
+
|
|
493
|
+
return aDescendant;
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* insert node after preceding
|
|
498
|
+
*
|
|
499
|
+
* @param {Element} node
|
|
500
|
+
* @param {Element} preceding - predicate function
|
|
501
|
+
*/
|
|
502
|
+
var insertAfter = function (node, preceding) {
|
|
503
|
+
var next = preceding.nextSibling, parent = preceding.parentNode;
|
|
504
|
+
if (next) {
|
|
505
|
+
parent.insertBefore(node, next);
|
|
506
|
+
} else {
|
|
507
|
+
parent.appendChild(node);
|
|
508
|
+
}
|
|
509
|
+
return node;
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* append elements.
|
|
514
|
+
*
|
|
515
|
+
* @param {Element} node
|
|
516
|
+
* @param {Collection} aChild
|
|
517
|
+
*/
|
|
518
|
+
var appends = function (node, aChild) {
|
|
519
|
+
$.each(aChild, function (idx, child) {
|
|
520
|
+
node.appendChild(child);
|
|
521
|
+
});
|
|
522
|
+
return node;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
var isText = makePredByNodeName('#text');
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* returns #text's text size or element's childNodes size
|
|
529
|
+
*
|
|
530
|
+
* @param {Element} node
|
|
531
|
+
*/
|
|
532
|
+
var length = function (node) {
|
|
533
|
+
if (isText(node)) { return node.nodeValue.length; }
|
|
534
|
+
return node.childNodes.length;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* returns offset from parent.
|
|
539
|
+
*
|
|
540
|
+
* @param {Element} node
|
|
541
|
+
*/
|
|
542
|
+
var position = function (node) {
|
|
543
|
+
var offset = 0;
|
|
544
|
+
while ((node = node.previousSibling)) { offset += 1; }
|
|
545
|
+
return offset;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* return offsetPath(array of offset) from ancestor
|
|
550
|
+
*
|
|
551
|
+
* @param {Element} ancestor - ancestor node
|
|
552
|
+
* @param {Element} node
|
|
553
|
+
*/
|
|
554
|
+
var makeOffsetPath = function (ancestor, node) {
|
|
555
|
+
var aAncestor = list.initial(listAncestor(node, func.eq(ancestor)));
|
|
556
|
+
return $.map(aAncestor, position).reverse();
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* return element from offsetPath(array of offset)
|
|
561
|
+
*
|
|
562
|
+
* @param {Element} ancestor - ancestor node
|
|
563
|
+
* @param {array} aOffset - offsetPath
|
|
564
|
+
*/
|
|
565
|
+
var fromOffsetPath = function (ancestor, aOffset) {
|
|
566
|
+
var current = ancestor;
|
|
567
|
+
for (var i = 0, sz = aOffset.length; i < sz; i++) {
|
|
568
|
+
current = current.childNodes[aOffset[i]];
|
|
569
|
+
}
|
|
570
|
+
return current;
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* split element or #text
|
|
575
|
+
*
|
|
576
|
+
* @param {Element} node
|
|
577
|
+
* @param {Number} offset
|
|
578
|
+
*/
|
|
579
|
+
var splitData = function (node, offset) {
|
|
580
|
+
if (offset === 0) { return node; }
|
|
581
|
+
if (offset >= length(node)) { return node.nextSibling; }
|
|
582
|
+
|
|
583
|
+
// splitText
|
|
584
|
+
if (isText(node)) { return node.splitText(offset); }
|
|
585
|
+
|
|
586
|
+
// splitElement
|
|
587
|
+
var child = node.childNodes[offset];
|
|
588
|
+
node = insertAfter(node.cloneNode(false), node);
|
|
589
|
+
return appends(node, listNext(child));
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* split dom tree by boundaryPoint(pivot and offset)
|
|
594
|
+
*
|
|
595
|
+
* @param {Element} root
|
|
596
|
+
* @param {Element} pivot - this will be boundaryPoint's node
|
|
597
|
+
* @param {Number} offset - this will be boundaryPoint's offset
|
|
598
|
+
*/
|
|
599
|
+
var split = function (root, pivot, offset) {
|
|
600
|
+
var aAncestor = listAncestor(pivot, func.eq(root));
|
|
601
|
+
if (aAncestor.length === 1) { return splitData(pivot, offset); }
|
|
602
|
+
return aAncestor.reduce(function (node, parent) {
|
|
603
|
+
var clone = parent.cloneNode(false);
|
|
604
|
+
insertAfter(clone, parent);
|
|
605
|
+
if (node === pivot) {
|
|
606
|
+
node = splitData(node, offset);
|
|
607
|
+
}
|
|
608
|
+
appends(clone, listNext(node));
|
|
609
|
+
return clone;
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* remove node, (bRemoveChild: remove child or not)
|
|
615
|
+
* @param {Element} node
|
|
616
|
+
* @param {Boolean} bRemoveChild
|
|
617
|
+
*/
|
|
618
|
+
var remove = function (node, bRemoveChild) {
|
|
619
|
+
if (!node || !node.parentNode) { return; }
|
|
620
|
+
if (node.removeNode) { return node.removeNode(bRemoveChild); }
|
|
621
|
+
|
|
622
|
+
var elParent = node.parentNode;
|
|
623
|
+
if (!bRemoveChild) {
|
|
624
|
+
var aNode = [];
|
|
625
|
+
var i, sz;
|
|
626
|
+
for (i = 0, sz = node.childNodes.length; i < sz; i++) {
|
|
627
|
+
aNode.push(node.childNodes[i]);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
for (i = 0, sz = aNode.length; i < sz; i++) {
|
|
631
|
+
elParent.insertBefore(aNode[i], node);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
elParent.removeChild(node);
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
var html = function ($node) {
|
|
639
|
+
return dom.isTextarea($node[0]) ? $node.val() : $node.html();
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
blank: agent.bMSIE ? ' ' : '<br/>',
|
|
644
|
+
emptyPara: '<p><br/></p>',
|
|
645
|
+
isEditable: isEditable,
|
|
646
|
+
isControlSizing: isControlSizing,
|
|
647
|
+
buildLayoutInfo: buildLayoutInfo,
|
|
648
|
+
isText: isText,
|
|
649
|
+
isPara: isPara,
|
|
650
|
+
isList: isList,
|
|
651
|
+
isTable: makePredByNodeName('TABLE'),
|
|
652
|
+
isCell: isCell,
|
|
653
|
+
isAnchor: makePredByNodeName('A'),
|
|
654
|
+
isDiv: makePredByNodeName('DIV'),
|
|
655
|
+
isLi: makePredByNodeName('LI'),
|
|
656
|
+
isSpan: makePredByNodeName('SPAN'),
|
|
657
|
+
isB: makePredByNodeName('B'),
|
|
658
|
+
isU: makePredByNodeName('U'),
|
|
659
|
+
isS: makePredByNodeName('S'),
|
|
660
|
+
isI: makePredByNodeName('I'),
|
|
661
|
+
isImg: makePredByNodeName('IMG'),
|
|
662
|
+
isTextarea: makePredByNodeName('TEXTAREA'),
|
|
663
|
+
ancestor: ancestor,
|
|
664
|
+
listAncestor: listAncestor,
|
|
665
|
+
listNext: listNext,
|
|
666
|
+
listPrev: listPrev,
|
|
667
|
+
listDescendant: listDescendant,
|
|
668
|
+
commonAncestor: commonAncestor,
|
|
669
|
+
listBetween: listBetween,
|
|
670
|
+
insertAfter: insertAfter,
|
|
671
|
+
position: position,
|
|
672
|
+
makeOffsetPath: makeOffsetPath,
|
|
673
|
+
fromOffsetPath: fromOffsetPath,
|
|
674
|
+
split: split,
|
|
675
|
+
remove: remove,
|
|
676
|
+
html: html
|
|
677
|
+
};
|
|
678
|
+
})();
|
|
679
|
+
|
|
680
|
+
var settings = {
|
|
681
|
+
// version
|
|
682
|
+
version: '0.5.2',
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* options
|
|
686
|
+
*/
|
|
687
|
+
options: {
|
|
688
|
+
width: null, // set editor width
|
|
689
|
+
height: null, // set editable height, ex) 300
|
|
690
|
+
|
|
691
|
+
focus: false, // set focus after initilize summernote
|
|
692
|
+
|
|
693
|
+
tabsize: 4, // size of tab ex) 2 or 4
|
|
694
|
+
styleWithSpan: true, // style with span (Chrome and FF only)
|
|
695
|
+
|
|
696
|
+
disableLinkTarget: false, // hide link Target Checkbox
|
|
697
|
+
disableDragAndDrop: false, // disable drag and drop event
|
|
698
|
+
|
|
699
|
+
codemirror: { // codemirror options
|
|
700
|
+
mode: 'text/html',
|
|
701
|
+
lineNumbers: true
|
|
702
|
+
},
|
|
703
|
+
|
|
704
|
+
// language
|
|
705
|
+
lang: 'en-US', // language 'en-US', 'ko-KR', ...
|
|
706
|
+
direction: null, // text direction, ex) 'rtl'
|
|
707
|
+
|
|
708
|
+
// toolbar
|
|
709
|
+
toolbar: [
|
|
710
|
+
['style', ['style']],
|
|
711
|
+
['font', ['bold', 'italic', 'underline', 'superscript', 'subscript', 'strikethrough', 'clear']],
|
|
712
|
+
['fontname', ['fontname']],
|
|
713
|
+
// ['fontsize', ['fontsize']], // Still buggy
|
|
714
|
+
['color', ['color']],
|
|
715
|
+
['para', ['ul', 'ol', 'paragraph']],
|
|
716
|
+
['height', ['height']],
|
|
717
|
+
['table', ['table']],
|
|
718
|
+
['insert', ['link', 'picture', 'video']],
|
|
719
|
+
['view', ['fullscreen', 'codeview']],
|
|
720
|
+
['help', ['help']]
|
|
721
|
+
],
|
|
722
|
+
|
|
723
|
+
// air mode: inline editor
|
|
724
|
+
airMode: false,
|
|
725
|
+
// airPopover: [
|
|
726
|
+
// ['style', ['style']],
|
|
727
|
+
// ['font', ['bold', 'italic', 'underline', 'clear']],
|
|
728
|
+
// ['fontname', ['fontname']],
|
|
729
|
+
// ['fontsize', ['fontsize']], // Still buggy
|
|
730
|
+
// ['color', ['color']],
|
|
731
|
+
// ['para', ['ul', 'ol', 'paragraph']],
|
|
732
|
+
// ['height', ['height']],
|
|
733
|
+
// ['table', ['table']],
|
|
734
|
+
// ['insert', ['link', 'picture', 'video']],
|
|
735
|
+
// ['help', ['help']]
|
|
736
|
+
// ],
|
|
737
|
+
airPopover: [
|
|
738
|
+
['color', ['color']],
|
|
739
|
+
['font', ['bold', 'underline', 'clear']],
|
|
740
|
+
['para', ['ul', 'paragraph']],
|
|
741
|
+
['table', ['table']],
|
|
742
|
+
['insert', ['link', 'picture']]
|
|
743
|
+
],
|
|
744
|
+
|
|
745
|
+
// style tag
|
|
746
|
+
styleTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
|
747
|
+
|
|
748
|
+
// default fontName
|
|
749
|
+
defaultFontName: 'Arial',
|
|
750
|
+
|
|
751
|
+
// fontName
|
|
752
|
+
fontNames: [
|
|
753
|
+
'Serif', 'Sans', 'Arial', 'Arial Black', 'Courier',
|
|
754
|
+
'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande',
|
|
755
|
+
'Lucida Sans', 'Tahoma', 'Times', 'Times New Roman', 'Verdana'
|
|
756
|
+
],
|
|
757
|
+
|
|
758
|
+
// pallete colors(n x n)
|
|
759
|
+
colors: [
|
|
760
|
+
['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
|
|
761
|
+
['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
|
|
762
|
+
['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
|
|
763
|
+
['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
|
|
764
|
+
['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
|
|
765
|
+
['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
|
|
766
|
+
['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
|
|
767
|
+
['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
|
|
768
|
+
],
|
|
769
|
+
|
|
770
|
+
// fontSize
|
|
771
|
+
fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
|
|
772
|
+
|
|
773
|
+
// lineHeight
|
|
774
|
+
lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
|
|
775
|
+
|
|
776
|
+
// callbacks
|
|
777
|
+
oninit: null, // initialize
|
|
778
|
+
onfocus: null, // editable has focus
|
|
779
|
+
onblur: null, // editable out of focus
|
|
780
|
+
onenter: null, // enter key pressed
|
|
781
|
+
onkeyup: null, // keyup
|
|
782
|
+
onkeydown: null, // keydown
|
|
783
|
+
onImageUpload: null, // imageUploadHandler
|
|
784
|
+
onImageUploadError: null, // imageUploadErrorHandler
|
|
785
|
+
onToolbarClick: null,
|
|
786
|
+
|
|
787
|
+
keyMap: {
|
|
788
|
+
pc: {
|
|
789
|
+
'CTRL+Z': 'undo',
|
|
790
|
+
'CTRL+Y': 'redo',
|
|
791
|
+
'TAB': 'tab',
|
|
792
|
+
'SHIFT+TAB': 'untab',
|
|
793
|
+
'CTRL+B': 'bold',
|
|
794
|
+
'CTRL+I': 'italic',
|
|
795
|
+
'CTRL+U': 'underline',
|
|
796
|
+
'CTRL+SHIFT+S': 'strikethrough',
|
|
797
|
+
'CTRL+BACKSLASH': 'removeFormat',
|
|
798
|
+
'CTRL+SHIFT+L': 'justifyLeft',
|
|
799
|
+
'CTRL+SHIFT+E': 'justifyCenter',
|
|
800
|
+
'CTRL+SHIFT+R': 'justifyRight',
|
|
801
|
+
'CTRL+SHIFT+J': 'justifyFull',
|
|
802
|
+
'CTRL+SHIFT+NUM7': 'insertUnorderedList',
|
|
803
|
+
'CTRL+SHIFT+NUM8': 'insertOrderedList',
|
|
804
|
+
'CTRL+LEFTBRACKET': 'outdent',
|
|
805
|
+
'CTRL+RIGHTBRACKET': 'indent',
|
|
806
|
+
'CTRL+NUM0': 'formatPara',
|
|
807
|
+
'CTRL+NUM1': 'formatH1',
|
|
808
|
+
'CTRL+NUM2': 'formatH2',
|
|
809
|
+
'CTRL+NUM3': 'formatH3',
|
|
810
|
+
'CTRL+NUM4': 'formatH4',
|
|
811
|
+
'CTRL+NUM5': 'formatH5',
|
|
812
|
+
'CTRL+NUM6': 'formatH6',
|
|
813
|
+
'CTRL+ENTER': 'insertHorizontalRule'
|
|
814
|
+
},
|
|
815
|
+
|
|
816
|
+
mac: {
|
|
817
|
+
'CMD+Z': 'undo',
|
|
818
|
+
'CMD+SHIFT+Z': 'redo',
|
|
819
|
+
'TAB': 'tab',
|
|
820
|
+
'SHIFT+TAB': 'untab',
|
|
821
|
+
'CMD+B': 'bold',
|
|
822
|
+
'CMD+I': 'italic',
|
|
823
|
+
'CMD+U': 'underline',
|
|
824
|
+
'CMD+SHIFT+S': 'strikethrough',
|
|
825
|
+
'CMD+BACKSLASH': 'removeFormat',
|
|
826
|
+
'CMD+SHIFT+L': 'justifyLeft',
|
|
827
|
+
'CMD+SHIFT+E': 'justifyCenter',
|
|
828
|
+
'CMD+SHIFT+R': 'justifyRight',
|
|
829
|
+
'CMD+SHIFT+J': 'justifyFull',
|
|
830
|
+
'CMD+SHIFT+NUM7': 'insertUnorderedList',
|
|
831
|
+
'CMD+SHIFT+NUM8': 'insertOrderedList',
|
|
832
|
+
'CMD+LEFTBRACKET': 'outdent',
|
|
833
|
+
'CMD+RIGHTBRACKET': 'indent',
|
|
834
|
+
'CMD+NUM0': 'formatPara',
|
|
835
|
+
'CMD+NUM1': 'formatH1',
|
|
836
|
+
'CMD+NUM2': 'formatH2',
|
|
837
|
+
'CMD+NUM3': 'formatH3',
|
|
838
|
+
'CMD+NUM4': 'formatH4',
|
|
839
|
+
'CMD+NUM5': 'formatH5',
|
|
840
|
+
'CMD+NUM6': 'formatH6',
|
|
841
|
+
'CMD+ENTER': 'insertHorizontalRule'
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
// default language: en-US
|
|
847
|
+
lang: {
|
|
848
|
+
'en-US': {
|
|
849
|
+
font: {
|
|
850
|
+
bold: 'Bold',
|
|
851
|
+
italic: 'Italic',
|
|
852
|
+
underline: 'Underline',
|
|
853
|
+
strikethrough: 'Strikethrough',
|
|
854
|
+
clear: 'Remove Font Style',
|
|
855
|
+
height: 'Line Height',
|
|
856
|
+
name: 'Font Family',
|
|
857
|
+
size: 'Font Size'
|
|
858
|
+
},
|
|
859
|
+
image: {
|
|
860
|
+
image: 'Picture',
|
|
861
|
+
insert: 'Insert Image',
|
|
862
|
+
resizeFull: 'Resize Full',
|
|
863
|
+
resizeHalf: 'Resize Half',
|
|
864
|
+
resizeQuarter: 'Resize Quarter',
|
|
865
|
+
floatLeft: 'Float Left',
|
|
866
|
+
floatRight: 'Float Right',
|
|
867
|
+
floatNone: 'Float None',
|
|
868
|
+
dragImageHere: 'Drag an image here',
|
|
869
|
+
selectFromFiles: 'Select from files',
|
|
870
|
+
url: 'Image URL',
|
|
871
|
+
remove: 'Remove Image'
|
|
872
|
+
},
|
|
873
|
+
link: {
|
|
874
|
+
link: 'Link',
|
|
875
|
+
insert: 'Insert Link',
|
|
876
|
+
unlink: 'Unlink',
|
|
877
|
+
edit: 'Edit',
|
|
878
|
+
textToDisplay: 'Text to display',
|
|
879
|
+
url: 'To what URL should this link go?',
|
|
880
|
+
openInNewWindow: 'Open in new window'
|
|
881
|
+
},
|
|
882
|
+
video: {
|
|
883
|
+
video: 'Video',
|
|
884
|
+
videoLink: 'Video Link',
|
|
885
|
+
insert: 'Insert Video',
|
|
886
|
+
url: 'Video URL?',
|
|
887
|
+
providers: '(YouTube, Vimeo, Vine, Instagram, or DailyMotion)'
|
|
888
|
+
},
|
|
889
|
+
table: {
|
|
890
|
+
table: 'Table'
|
|
891
|
+
},
|
|
892
|
+
hr: {
|
|
893
|
+
insert: 'Insert Horizontal Rule'
|
|
894
|
+
},
|
|
895
|
+
style: {
|
|
896
|
+
style: 'Style',
|
|
897
|
+
normal: 'Normal',
|
|
898
|
+
blockquote: 'Quote',
|
|
899
|
+
pre: 'Code',
|
|
900
|
+
h1: 'Header 1',
|
|
901
|
+
h2: 'Header 2',
|
|
902
|
+
h3: 'Header 3',
|
|
903
|
+
h4: 'Header 4',
|
|
904
|
+
h5: 'Header 5',
|
|
905
|
+
h6: 'Header 6'
|
|
906
|
+
},
|
|
907
|
+
lists: {
|
|
908
|
+
unordered: 'Unordered list',
|
|
909
|
+
ordered: 'Ordered list'
|
|
910
|
+
},
|
|
911
|
+
options: {
|
|
912
|
+
help: 'Help',
|
|
913
|
+
fullscreen: 'Full Screen',
|
|
914
|
+
codeview: 'Code View'
|
|
915
|
+
},
|
|
916
|
+
paragraph: {
|
|
917
|
+
paragraph: 'Paragraph',
|
|
918
|
+
outdent: 'Outdent',
|
|
919
|
+
indent: 'Indent',
|
|
920
|
+
left: 'Align left',
|
|
921
|
+
center: 'Align center',
|
|
922
|
+
right: 'Align right',
|
|
923
|
+
justify: 'Justify full'
|
|
924
|
+
},
|
|
925
|
+
color: {
|
|
926
|
+
recent: 'Recent Color',
|
|
927
|
+
more: 'More Color',
|
|
928
|
+
background: 'BackColor',
|
|
929
|
+
foreground: 'FontColor',
|
|
930
|
+
transparent: 'Transparent',
|
|
931
|
+
setTransparent: 'Set transparent',
|
|
932
|
+
reset: 'Reset',
|
|
933
|
+
resetToDefault: 'Reset to default'
|
|
934
|
+
},
|
|
935
|
+
shortcut: {
|
|
936
|
+
shortcuts: 'Keyboard shortcuts',
|
|
937
|
+
close: 'Close',
|
|
938
|
+
textFormatting: 'Text formatting',
|
|
939
|
+
action: 'Action',
|
|
940
|
+
paragraphFormatting: 'Paragraph formatting',
|
|
941
|
+
documentStyle: 'Document Style'
|
|
942
|
+
},
|
|
943
|
+
history: {
|
|
944
|
+
undo: 'Undo',
|
|
945
|
+
redo: 'Redo'
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
/**
|
|
952
|
+
* Async functions which returns `Promise`
|
|
953
|
+
*/
|
|
954
|
+
var async = (function () {
|
|
955
|
+
/**
|
|
956
|
+
* read contents of file as representing URL
|
|
957
|
+
*
|
|
958
|
+
* @param {File} file
|
|
959
|
+
* @return {Promise} - then: sDataUrl
|
|
960
|
+
*/
|
|
961
|
+
var readFileAsDataURL = function (file) {
|
|
962
|
+
return $.Deferred(function (deferred) {
|
|
963
|
+
$.extend(new FileReader(), {
|
|
964
|
+
onload: function (e) {
|
|
965
|
+
var sDataURL = e.target.result;
|
|
966
|
+
deferred.resolve(sDataURL);
|
|
967
|
+
},
|
|
968
|
+
onerror: function () {
|
|
969
|
+
deferred.reject(this);
|
|
970
|
+
}
|
|
971
|
+
}).readAsDataURL(file);
|
|
972
|
+
}).promise();
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* create `<image>` from url string
|
|
977
|
+
*
|
|
978
|
+
* @param {String} sUrl
|
|
979
|
+
* @return {Promise} - then: $image
|
|
980
|
+
*/
|
|
981
|
+
var createImage = function (sUrl) {
|
|
982
|
+
return $.Deferred(function (deferred) {
|
|
983
|
+
$('<img>').one('load', function () {
|
|
984
|
+
deferred.resolve($(this));
|
|
985
|
+
}).one('error abort', function () {
|
|
986
|
+
deferred.reject($(this));
|
|
987
|
+
}).css({
|
|
988
|
+
display: 'none'
|
|
989
|
+
}).appendTo(document.body).attr('src', sUrl);
|
|
990
|
+
}).promise();
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
return {
|
|
994
|
+
readFileAsDataURL: readFileAsDataURL,
|
|
995
|
+
createImage: createImage
|
|
996
|
+
};
|
|
997
|
+
})();
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Object for keycodes.
|
|
1001
|
+
*/
|
|
1002
|
+
var key = {
|
|
1003
|
+
isEdit: function (keyCode) {
|
|
1004
|
+
return [8, 9, 13, 32].indexOf(keyCode) !== -1;
|
|
1005
|
+
},
|
|
1006
|
+
nameFromCode: {
|
|
1007
|
+
'8': 'BACKSPACE',
|
|
1008
|
+
'9': 'TAB',
|
|
1009
|
+
'13': 'ENTER',
|
|
1010
|
+
'32': 'SPACE',
|
|
1011
|
+
|
|
1012
|
+
// Number: 0-9
|
|
1013
|
+
'48': 'NUM0',
|
|
1014
|
+
'49': 'NUM1',
|
|
1015
|
+
'50': 'NUM2',
|
|
1016
|
+
'51': 'NUM3',
|
|
1017
|
+
'52': 'NUM4',
|
|
1018
|
+
'53': 'NUM5',
|
|
1019
|
+
'54': 'NUM6',
|
|
1020
|
+
'55': 'NUM7',
|
|
1021
|
+
'56': 'NUM8',
|
|
1022
|
+
|
|
1023
|
+
// Alphabet: a-z
|
|
1024
|
+
'66': 'B',
|
|
1025
|
+
'69': 'E',
|
|
1026
|
+
'73': 'I',
|
|
1027
|
+
'74': 'J',
|
|
1028
|
+
'75': 'K',
|
|
1029
|
+
'76': 'L',
|
|
1030
|
+
'82': 'R',
|
|
1031
|
+
'83': 'S',
|
|
1032
|
+
'85': 'U',
|
|
1033
|
+
'89': 'Y',
|
|
1034
|
+
'90': 'Z',
|
|
1035
|
+
|
|
1036
|
+
'191': 'SLASH',
|
|
1037
|
+
'219': 'LEFTBRACKET',
|
|
1038
|
+
'220': 'BACKSLASH',
|
|
1039
|
+
'221': 'RIGHTBRACKET'
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Style
|
|
1045
|
+
* @class
|
|
1046
|
+
*/
|
|
1047
|
+
var Style = function () {
|
|
1048
|
+
/**
|
|
1049
|
+
* passing an array of style properties to .css()
|
|
1050
|
+
* will result in an object of property-value pairs.
|
|
1051
|
+
* (compability with version < 1.9)
|
|
1052
|
+
*
|
|
1053
|
+
* @param {jQuery} $obj
|
|
1054
|
+
* @param {Array} propertyNames - An array of one or more CSS properties.
|
|
1055
|
+
* @returns {Object}
|
|
1056
|
+
*/
|
|
1057
|
+
var jQueryCSS = function ($obj, propertyNames) {
|
|
1058
|
+
if (agent.jqueryVersion < 1.9) {
|
|
1059
|
+
var result = {};
|
|
1060
|
+
$.each(propertyNames, function (idx, propertyName) {
|
|
1061
|
+
result[propertyName] = $obj.css(propertyName);
|
|
1062
|
+
});
|
|
1063
|
+
return result;
|
|
1064
|
+
}
|
|
1065
|
+
return $obj.css.call($obj, propertyNames);
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* paragraph level style
|
|
1070
|
+
*
|
|
1071
|
+
* @param {WrappedRange} rng
|
|
1072
|
+
* @param {Object} oStyle
|
|
1073
|
+
*/
|
|
1074
|
+
this.stylePara = function (rng, oStyle) {
|
|
1075
|
+
$.each(rng.nodes(dom.isPara), function (idx, elPara) {
|
|
1076
|
+
$(elPara).css(oStyle);
|
|
1077
|
+
});
|
|
1078
|
+
};
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* get current style on cursor
|
|
1082
|
+
*
|
|
1083
|
+
* @param {WrappedRange} rng
|
|
1084
|
+
* @param {Element} elTarget - target element on event
|
|
1085
|
+
* @return {Object} - object contains style properties.
|
|
1086
|
+
*/
|
|
1087
|
+
this.current = function (rng, elTarget) {
|
|
1088
|
+
var $cont = $(dom.isText(rng.sc) ? rng.sc.parentNode : rng.sc);
|
|
1089
|
+
var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
|
|
1090
|
+
var oStyle = jQueryCSS($cont, properties) || {};
|
|
1091
|
+
|
|
1092
|
+
oStyle['font-size'] = parseInt(oStyle['font-size'], 10);
|
|
1093
|
+
|
|
1094
|
+
// document.queryCommandState for toggle state
|
|
1095
|
+
oStyle['font-bold'] = document.queryCommandState('bold') ? 'bold' : 'normal';
|
|
1096
|
+
oStyle['font-italic'] = document.queryCommandState('italic') ? 'italic' : 'normal';
|
|
1097
|
+
oStyle['font-underline'] = document.queryCommandState('underline') ? 'underline' : 'normal';
|
|
1098
|
+
oStyle['font-strikethrough'] = document.queryCommandState('strikeThrough') ? 'strikethrough' : 'normal';
|
|
1099
|
+
oStyle['font-superscript'] = document.queryCommandState('superscript') ? 'superscript' : 'normal';
|
|
1100
|
+
oStyle['font-subscript'] = document.queryCommandState('subscript') ? 'subscript' : 'normal';
|
|
1101
|
+
|
|
1102
|
+
// list-style-type to list-style(unordered, ordered)
|
|
1103
|
+
if (!rng.isOnList()) {
|
|
1104
|
+
oStyle['list-style'] = 'none';
|
|
1105
|
+
} else {
|
|
1106
|
+
var aOrderedType = ['circle', 'disc', 'disc-leading-zero', 'square'];
|
|
1107
|
+
var bUnordered = $.inArray(oStyle['list-style-type'], aOrderedType) > -1;
|
|
1108
|
+
oStyle['list-style'] = bUnordered ? 'unordered' : 'ordered';
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
var elPara = dom.ancestor(rng.sc, dom.isPara);
|
|
1112
|
+
if (elPara && elPara.style['line-height']) {
|
|
1113
|
+
oStyle['line-height'] = elPara.style.lineHeight;
|
|
1114
|
+
} else {
|
|
1115
|
+
var lineHeight = parseInt(oStyle['line-height'], 10) / parseInt(oStyle['font-size'], 10);
|
|
1116
|
+
oStyle['line-height'] = lineHeight.toFixed(1);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
oStyle.image = dom.isImg(elTarget) && elTarget;
|
|
1120
|
+
oStyle.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
|
|
1121
|
+
oStyle.aAncestor = dom.listAncestor(rng.sc, dom.isEditable);
|
|
1122
|
+
oStyle.range = rng;
|
|
1123
|
+
|
|
1124
|
+
return oStyle;
|
|
1125
|
+
};
|
|
1126
|
+
};
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* range module
|
|
1130
|
+
*/
|
|
1131
|
+
var range = (function () {
|
|
1132
|
+
var bW3CRangeSupport = !!document.createRange;
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
|
|
1136
|
+
* @param {TextRange} textRange
|
|
1137
|
+
* @param {Boolean} bStart
|
|
1138
|
+
* @return {BoundaryPoint}
|
|
1139
|
+
*/
|
|
1140
|
+
var textRange2bp = function (textRange, bStart) {
|
|
1141
|
+
var elCont = textRange.parentElement(), nOffset;
|
|
1142
|
+
|
|
1143
|
+
var tester = document.body.createTextRange(), elPrevCont;
|
|
1144
|
+
var aChild = list.from(elCont.childNodes);
|
|
1145
|
+
for (nOffset = 0; nOffset < aChild.length; nOffset++) {
|
|
1146
|
+
if (dom.isText(aChild[nOffset])) { continue; }
|
|
1147
|
+
tester.moveToElementText(aChild[nOffset]);
|
|
1148
|
+
if (tester.compareEndPoints('StartToStart', textRange) >= 0) { break; }
|
|
1149
|
+
elPrevCont = aChild[nOffset];
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (nOffset !== 0 && dom.isText(aChild[nOffset - 1])) {
|
|
1153
|
+
var textRangeStart = document.body.createTextRange(), elCurText = null;
|
|
1154
|
+
textRangeStart.moveToElementText(elPrevCont || elCont);
|
|
1155
|
+
textRangeStart.collapse(!elPrevCont);
|
|
1156
|
+
elCurText = elPrevCont ? elPrevCont.nextSibling : elCont.firstChild;
|
|
1157
|
+
|
|
1158
|
+
var pointTester = textRange.duplicate();
|
|
1159
|
+
pointTester.setEndPoint('StartToStart', textRangeStart);
|
|
1160
|
+
var nTextCount = pointTester.text.replace(/[\r\n]/g, '').length;
|
|
1161
|
+
|
|
1162
|
+
while (nTextCount > elCurText.nodeValue.length && elCurText.nextSibling) {
|
|
1163
|
+
nTextCount -= elCurText.nodeValue.length;
|
|
1164
|
+
elCurText = elCurText.nextSibling;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/* jshint ignore:start */
|
|
1168
|
+
var sDummy = elCurText.nodeValue; //enforce IE to re-reference elCurText, hack
|
|
1169
|
+
/* jshint ignore:end */
|
|
1170
|
+
|
|
1171
|
+
if (bStart && elCurText.nextSibling && dom.isText(elCurText.nextSibling) &&
|
|
1172
|
+
nTextCount === elCurText.nodeValue.length) {
|
|
1173
|
+
nTextCount -= elCurText.nodeValue.length;
|
|
1174
|
+
elCurText = elCurText.nextSibling;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
elCont = elCurText;
|
|
1178
|
+
nOffset = nTextCount;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
return {cont: elCont, offset: nOffset};
|
|
1182
|
+
};
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* return TextRange from boundary point (inspired by google closure-library)
|
|
1186
|
+
* @param {BoundaryPoint} bp
|
|
1187
|
+
* @return {TextRange}
|
|
1188
|
+
*/
|
|
1189
|
+
var bp2textRange = function (bp) {
|
|
1190
|
+
var textRangeInfo = function (elCont, nOffset) {
|
|
1191
|
+
var elNode, bCollapseToStart;
|
|
1192
|
+
|
|
1193
|
+
if (dom.isText(elCont)) {
|
|
1194
|
+
var aPrevText = dom.listPrev(elCont, func.not(dom.isText));
|
|
1195
|
+
var elPrevCont = list.last(aPrevText).previousSibling;
|
|
1196
|
+
elNode = elPrevCont || elCont.parentNode;
|
|
1197
|
+
nOffset += list.sum(list.tail(aPrevText), dom.length);
|
|
1198
|
+
bCollapseToStart = !elPrevCont;
|
|
1199
|
+
} else {
|
|
1200
|
+
elNode = elCont.childNodes[nOffset] || elCont;
|
|
1201
|
+
if (dom.isText(elNode)) {
|
|
1202
|
+
return textRangeInfo(elNode, nOffset);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
nOffset = 0;
|
|
1206
|
+
bCollapseToStart = false;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
return {cont: elNode, collapseToStart: bCollapseToStart, offset: nOffset};
|
|
1210
|
+
};
|
|
1211
|
+
|
|
1212
|
+
var textRange = document.body.createTextRange();
|
|
1213
|
+
var info = textRangeInfo(bp.cont, bp.offset);
|
|
1214
|
+
|
|
1215
|
+
textRange.moveToElementText(info.cont);
|
|
1216
|
+
textRange.collapse(info.collapseToStart);
|
|
1217
|
+
textRange.moveStart('character', info.offset);
|
|
1218
|
+
return textRange;
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Wrapped Range
|
|
1223
|
+
*
|
|
1224
|
+
* @param {Element} sc - start container
|
|
1225
|
+
* @param {Number} so - start offset
|
|
1226
|
+
* @param {Element} ec - end container
|
|
1227
|
+
* @param {Number} eo - end offset
|
|
1228
|
+
*/
|
|
1229
|
+
var WrappedRange = function (sc, so, ec, eo) {
|
|
1230
|
+
this.sc = sc;
|
|
1231
|
+
this.so = so;
|
|
1232
|
+
this.ec = ec;
|
|
1233
|
+
this.eo = eo;
|
|
1234
|
+
|
|
1235
|
+
// nativeRange: get nativeRange from sc, so, ec, eo
|
|
1236
|
+
var nativeRange = function () {
|
|
1237
|
+
if (bW3CRangeSupport) {
|
|
1238
|
+
var w3cRange = document.createRange();
|
|
1239
|
+
w3cRange.setStart(sc, so);
|
|
1240
|
+
w3cRange.setEnd(ec, eo);
|
|
1241
|
+
return w3cRange;
|
|
1242
|
+
} else {
|
|
1243
|
+
var textRange = bp2textRange({cont: sc, offset: so});
|
|
1244
|
+
textRange.setEndPoint('EndToEnd', bp2textRange({cont: ec, offset: eo}));
|
|
1245
|
+
return textRange;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* select update visible range
|
|
1251
|
+
*/
|
|
1252
|
+
this.select = function () {
|
|
1253
|
+
var nativeRng = nativeRange();
|
|
1254
|
+
if (bW3CRangeSupport) {
|
|
1255
|
+
var selection = document.getSelection();
|
|
1256
|
+
if (selection.rangeCount > 0) { selection.removeAllRanges(); }
|
|
1257
|
+
selection.addRange(nativeRng);
|
|
1258
|
+
} else {
|
|
1259
|
+
nativeRng.select();
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* returns matched nodes on range
|
|
1265
|
+
*
|
|
1266
|
+
* @param {Function} pred - predicate function
|
|
1267
|
+
* @return {Element[]}
|
|
1268
|
+
*/
|
|
1269
|
+
this.nodes = function (pred) {
|
|
1270
|
+
var aNode = dom.listBetween(sc, ec);
|
|
1271
|
+
var aMatched = list.compact($.map(aNode, function (node) {
|
|
1272
|
+
return dom.ancestor(node, pred);
|
|
1273
|
+
}));
|
|
1274
|
+
return $.map(list.clusterBy(aMatched, func.eq2), list.head);
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* returns commonAncestor of range
|
|
1279
|
+
* @return {Element} - commonAncestor
|
|
1280
|
+
*/
|
|
1281
|
+
this.commonAncestor = function () {
|
|
1282
|
+
return dom.commonAncestor(sc, ec);
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* makeIsOn: return isOn(pred) function
|
|
1287
|
+
*/
|
|
1288
|
+
var makeIsOn = function (pred) {
|
|
1289
|
+
return function () {
|
|
1290
|
+
var elAncestor = dom.ancestor(sc, pred);
|
|
1291
|
+
return !!elAncestor && (elAncestor === dom.ancestor(ec, pred));
|
|
1292
|
+
};
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
// isOnEditable: judge whether range is on editable or not
|
|
1296
|
+
this.isOnEditable = makeIsOn(dom.isEditable);
|
|
1297
|
+
// isOnList: judge whether range is on list node or not
|
|
1298
|
+
this.isOnList = makeIsOn(dom.isList);
|
|
1299
|
+
// isOnAnchor: judge whether range is on anchor node or not
|
|
1300
|
+
this.isOnAnchor = makeIsOn(dom.isAnchor);
|
|
1301
|
+
// isOnAnchor: judge whether range is on cell node or not
|
|
1302
|
+
this.isOnCell = makeIsOn(dom.isCell);
|
|
1303
|
+
// isCollapsed: judge whether range was collapsed
|
|
1304
|
+
this.isCollapsed = function () { return sc === ec && so === eo; };
|
|
1305
|
+
|
|
1306
|
+
/**
|
|
1307
|
+
* insert node at current cursor
|
|
1308
|
+
* @param {Element} node
|
|
1309
|
+
*/
|
|
1310
|
+
this.insertNode = function (node) {
|
|
1311
|
+
var nativeRng = nativeRange();
|
|
1312
|
+
if (bW3CRangeSupport) {
|
|
1313
|
+
nativeRng.insertNode(node);
|
|
1314
|
+
} else {
|
|
1315
|
+
nativeRng.pasteHTML(node.outerHTML); // NOTE: missing node reference.
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
|
|
1319
|
+
this.toString = function () {
|
|
1320
|
+
var nativeRng = nativeRange();
|
|
1321
|
+
return bW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* create offsetPath bookmark
|
|
1326
|
+
* @param {Element} elEditable
|
|
1327
|
+
*/
|
|
1328
|
+
this.bookmark = function (elEditable) {
|
|
1329
|
+
return {
|
|
1330
|
+
s: { path: dom.makeOffsetPath(elEditable, sc), offset: so },
|
|
1331
|
+
e: { path: dom.makeOffsetPath(elEditable, ec), offset: eo }
|
|
1332
|
+
};
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
/**
|
|
1336
|
+
* getClientRects
|
|
1337
|
+
* @return {Rect[]}
|
|
1338
|
+
*/
|
|
1339
|
+
this.getClientRects = function () {
|
|
1340
|
+
var nativeRng = nativeRange();
|
|
1341
|
+
return nativeRng.getClientRects();
|
|
1342
|
+
};
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
return {
|
|
1346
|
+
/**
|
|
1347
|
+
* create Range Object From arguments or Browser Selection
|
|
1348
|
+
*
|
|
1349
|
+
* @param {Element} sc - start container
|
|
1350
|
+
* @param {Number} so - start offset
|
|
1351
|
+
* @param {Element} ec - end container
|
|
1352
|
+
* @param {Number} eo - end offset
|
|
1353
|
+
*/
|
|
1354
|
+
create : function (sc, so, ec, eo) {
|
|
1355
|
+
if (arguments.length === 0) { // from Browser Selection
|
|
1356
|
+
if (bW3CRangeSupport) { // webkit, firefox
|
|
1357
|
+
var selection = document.getSelection();
|
|
1358
|
+
if (selection.rangeCount === 0) { return null; }
|
|
1359
|
+
|
|
1360
|
+
var nativeRng = selection.getRangeAt(0);
|
|
1361
|
+
sc = nativeRng.startContainer;
|
|
1362
|
+
so = nativeRng.startOffset;
|
|
1363
|
+
ec = nativeRng.endContainer;
|
|
1364
|
+
eo = nativeRng.endOffset;
|
|
1365
|
+
} else { // IE8: TextRange
|
|
1366
|
+
var textRange = document.selection.createRange();
|
|
1367
|
+
var textRangeEnd = textRange.duplicate();
|
|
1368
|
+
textRangeEnd.collapse(false);
|
|
1369
|
+
var textRangeStart = textRange;
|
|
1370
|
+
textRangeStart.collapse(true);
|
|
1371
|
+
|
|
1372
|
+
var bpStart = textRange2bp(textRangeStart, true),
|
|
1373
|
+
bpEnd = textRange2bp(textRangeEnd, false);
|
|
1374
|
+
|
|
1375
|
+
sc = bpStart.cont;
|
|
1376
|
+
so = bpStart.offset;
|
|
1377
|
+
ec = bpEnd.cont;
|
|
1378
|
+
eo = bpEnd.offset;
|
|
1379
|
+
}
|
|
1380
|
+
} else if (arguments.length === 2) { //collapsed
|
|
1381
|
+
ec = sc;
|
|
1382
|
+
eo = so;
|
|
1383
|
+
}
|
|
1384
|
+
return new WrappedRange(sc, so, ec, eo);
|
|
1385
|
+
},
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* create WrappedRange from node
|
|
1389
|
+
*
|
|
1390
|
+
* @param {Element} node
|
|
1391
|
+
* @return {WrappedRange}
|
|
1392
|
+
*/
|
|
1393
|
+
createFromNode: function (node) {
|
|
1394
|
+
return this.create(node, 0, node, 1);
|
|
1395
|
+
},
|
|
1396
|
+
|
|
1397
|
+
/**
|
|
1398
|
+
* create WrappedRange from Bookmark
|
|
1399
|
+
*
|
|
1400
|
+
* @param {Element} elEditable
|
|
1401
|
+
* @param {Obkect} bookmark
|
|
1402
|
+
* @return {WrappedRange}
|
|
1403
|
+
*/
|
|
1404
|
+
createFromBookmark : function (elEditable, bookmark) {
|
|
1405
|
+
var sc = dom.fromOffsetPath(elEditable, bookmark.s.path);
|
|
1406
|
+
var so = bookmark.s.offset;
|
|
1407
|
+
var ec = dom.fromOffsetPath(elEditable, bookmark.e.path);
|
|
1408
|
+
var eo = bookmark.e.offset;
|
|
1409
|
+
return new WrappedRange(sc, so, ec, eo);
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
})();
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Table
|
|
1416
|
+
* @class
|
|
1417
|
+
*/
|
|
1418
|
+
var Table = function () {
|
|
1419
|
+
/**
|
|
1420
|
+
* handle tab key
|
|
1421
|
+
*
|
|
1422
|
+
* @param {WrappedRange} rng
|
|
1423
|
+
* @param {Boolean} bShift
|
|
1424
|
+
*/
|
|
1425
|
+
this.tab = function (rng, bShift) {
|
|
1426
|
+
var elCell = dom.ancestor(rng.commonAncestor(), dom.isCell);
|
|
1427
|
+
var elTable = dom.ancestor(elCell, dom.isTable);
|
|
1428
|
+
var aCell = dom.listDescendant(elTable, dom.isCell);
|
|
1429
|
+
|
|
1430
|
+
var elNext = list[bShift ? 'prev' : 'next'](aCell, elCell);
|
|
1431
|
+
if (elNext) {
|
|
1432
|
+
range.create(elNext, 0).select();
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* create empty table element
|
|
1438
|
+
*
|
|
1439
|
+
* @param {Number} nRow
|
|
1440
|
+
* @param {Number} nCol
|
|
1441
|
+
*/
|
|
1442
|
+
this.createTable = function (nCol, nRow) {
|
|
1443
|
+
var aTD = [], sTD;
|
|
1444
|
+
for (var idxCol = 0; idxCol < nCol; idxCol++) {
|
|
1445
|
+
aTD.push('<td>' + dom.blank + '</td>');
|
|
1446
|
+
}
|
|
1447
|
+
sTD = aTD.join('');
|
|
1448
|
+
|
|
1449
|
+
var aTR = [], sTR;
|
|
1450
|
+
for (var idxRow = 0; idxRow < nRow; idxRow++) {
|
|
1451
|
+
aTR.push('<tr>' + sTD + '</tr>');
|
|
1452
|
+
}
|
|
1453
|
+
sTR = aTR.join('');
|
|
1454
|
+
var sTable = '<table class="table table-bordered">' + sTR + '</table>';
|
|
1455
|
+
|
|
1456
|
+
return $(sTable)[0];
|
|
1457
|
+
};
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Editor
|
|
1462
|
+
* @class
|
|
1463
|
+
*/
|
|
1464
|
+
var Editor = function () {
|
|
1465
|
+
|
|
1466
|
+
var style = new Style();
|
|
1467
|
+
var table = new Table();
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* save current range
|
|
1471
|
+
*
|
|
1472
|
+
* @param {jQuery} $editable
|
|
1473
|
+
*/
|
|
1474
|
+
this.saveRange = function ($editable) {
|
|
1475
|
+
$editable.data('range', range.create());
|
|
1476
|
+
};
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
* restore lately range
|
|
1480
|
+
*
|
|
1481
|
+
* @param {jQuery} $editable
|
|
1482
|
+
*/
|
|
1483
|
+
this.restoreRange = function ($editable) {
|
|
1484
|
+
var rng = $editable.data('range');
|
|
1485
|
+
if (rng) { rng.select(); }
|
|
1486
|
+
};
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* current style
|
|
1490
|
+
* @param {Element} elTarget
|
|
1491
|
+
*/
|
|
1492
|
+
this.currentStyle = function (elTarget) {
|
|
1493
|
+
var rng = range.create();
|
|
1494
|
+
return rng.isOnEditable() && style.current(rng, elTarget);
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
/**
|
|
1498
|
+
* undo
|
|
1499
|
+
* @param {jQuery} $editable
|
|
1500
|
+
*/
|
|
1501
|
+
this.undo = function ($editable) {
|
|
1502
|
+
$editable.data('NoteHistory').undo($editable);
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
/**
|
|
1506
|
+
* redo
|
|
1507
|
+
* @param {jQuery} $editable
|
|
1508
|
+
*/
|
|
1509
|
+
this.redo = function ($editable) {
|
|
1510
|
+
$editable.data('NoteHistory').redo($editable);
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* record Undo
|
|
1515
|
+
* @param {jQuery} $editable
|
|
1516
|
+
*/
|
|
1517
|
+
var recordUndo = this.recordUndo = function ($editable) {
|
|
1518
|
+
$editable.data('NoteHistory').recordUndo($editable);
|
|
1519
|
+
};
|
|
1520
|
+
|
|
1521
|
+
/* jshint ignore:start */
|
|
1522
|
+
// native commands(with execCommand), generate function for execCommand
|
|
1523
|
+
var aCmd = ['bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
|
|
1524
|
+
'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
|
|
1525
|
+
'insertOrderedList', 'insertUnorderedList',
|
|
1526
|
+
'indent', 'outdent', 'formatBlock', 'removeFormat',
|
|
1527
|
+
'backColor', 'foreColor', 'insertHorizontalRule', 'fontName'];
|
|
1528
|
+
|
|
1529
|
+
for (var idx = 0, len = aCmd.length; idx < len; idx ++) {
|
|
1530
|
+
this[aCmd[idx]] = (function (sCmd) {
|
|
1531
|
+
return function ($editable, sValue) {
|
|
1532
|
+
recordUndo($editable);
|
|
1533
|
+
document.execCommand(sCmd, false, sValue);
|
|
1534
|
+
};
|
|
1535
|
+
})(aCmd[idx]);
|
|
1536
|
+
}
|
|
1537
|
+
/* jshint ignore:end */
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* @param {jQuery} $editable
|
|
1541
|
+
* @param {WrappedRange} rng
|
|
1542
|
+
* @param {Number} nTabsize
|
|
1543
|
+
*/
|
|
1544
|
+
var insertTab = function ($editable, rng, nTabsize) {
|
|
1545
|
+
recordUndo($editable);
|
|
1546
|
+
var sNbsp = new Array(nTabsize + 1).join(' ');
|
|
1547
|
+
rng.insertNode($('<span id="noteTab">' + sNbsp + '</span>')[0]);
|
|
1548
|
+
var $tab = $('#noteTab').removeAttr('id');
|
|
1549
|
+
rng = range.create($tab[0], 1);
|
|
1550
|
+
rng.select();
|
|
1551
|
+
dom.remove($tab[0]);
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* handle tab key
|
|
1556
|
+
* @param {jQuery} $editable
|
|
1557
|
+
* @param {Number} nTabsize
|
|
1558
|
+
* @param {Boolean} bShift
|
|
1559
|
+
*/
|
|
1560
|
+
this.tab = function ($editable, options) {
|
|
1561
|
+
var rng = range.create();
|
|
1562
|
+
if (rng.isCollapsed() && rng.isOnCell()) {
|
|
1563
|
+
table.tab(rng);
|
|
1564
|
+
} else {
|
|
1565
|
+
insertTab($editable, rng, options.tabsize);
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* handle shift+tab key
|
|
1571
|
+
*/
|
|
1572
|
+
this.untab = function () {
|
|
1573
|
+
var rng = range.create();
|
|
1574
|
+
if (rng.isCollapsed() && rng.isOnCell()) {
|
|
1575
|
+
table.tab(rng, true);
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* insert image
|
|
1581
|
+
*
|
|
1582
|
+
* @param {jQuery} $editable
|
|
1583
|
+
* @param {String} sUrl
|
|
1584
|
+
*/
|
|
1585
|
+
this.insertImage = function ($editable, sUrl) {
|
|
1586
|
+
async.createImage(sUrl).then(function ($image) {
|
|
1587
|
+
recordUndo($editable);
|
|
1588
|
+
$image.css({
|
|
1589
|
+
display: '',
|
|
1590
|
+
width: Math.min($editable.width(), $image.width())
|
|
1591
|
+
});
|
|
1592
|
+
range.create().insertNode($image[0]);
|
|
1593
|
+
}).fail(function () {
|
|
1594
|
+
var callbacks = $editable.data('callbacks');
|
|
1595
|
+
if (callbacks.onImageUploadError) {
|
|
1596
|
+
callbacks.onImageUploadError();
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
};
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* insert video
|
|
1603
|
+
* @param {jQuery} $editable
|
|
1604
|
+
* @param {String} sUrl
|
|
1605
|
+
*/
|
|
1606
|
+
this.insertVideo = function ($editable, sUrl) {
|
|
1607
|
+
recordUndo($editable);
|
|
1608
|
+
|
|
1609
|
+
// video url patterns(youtube, instagram, vimeo, dailymotion)
|
|
1610
|
+
var ytRegExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
|
|
1611
|
+
var ytMatch = sUrl.match(ytRegExp);
|
|
1612
|
+
|
|
1613
|
+
var igRegExp = /\/\/instagram.com\/p\/(.[a-zA-Z0-9]*)/;
|
|
1614
|
+
var igMatch = sUrl.match(igRegExp);
|
|
1615
|
+
|
|
1616
|
+
var vRegExp = /\/\/vine.co\/v\/(.[a-zA-Z0-9]*)/;
|
|
1617
|
+
var vMatch = sUrl.match(vRegExp);
|
|
1618
|
+
|
|
1619
|
+
var vimRegExp = /\/\/(player.)?vimeo.com\/([a-z]*\/)*([0-9]{6,11})[?]?.*/;
|
|
1620
|
+
var vimMatch = sUrl.match(vimRegExp);
|
|
1621
|
+
|
|
1622
|
+
var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
|
|
1623
|
+
var dmMatch = sUrl.match(dmRegExp);
|
|
1624
|
+
|
|
1625
|
+
var $video;
|
|
1626
|
+
if (ytMatch && ytMatch[2].length === 11) {
|
|
1627
|
+
var youtubeId = ytMatch[2];
|
|
1628
|
+
$video = $('<iframe>')
|
|
1629
|
+
.attr('src', '//www.youtube.com/embed/' + youtubeId)
|
|
1630
|
+
.attr('width', '640').attr('height', '360');
|
|
1631
|
+
} else if (igMatch && igMatch[0].length > 0) {
|
|
1632
|
+
$video = $('<iframe>')
|
|
1633
|
+
.attr('src', igMatch[0] + '/embed/')
|
|
1634
|
+
.attr('width', '612').attr('height', '710')
|
|
1635
|
+
.attr('scrolling', 'no')
|
|
1636
|
+
.attr('allowtransparency', 'true');
|
|
1637
|
+
} else if (vMatch && vMatch[0].length > 0) {
|
|
1638
|
+
$video = $('<iframe>')
|
|
1639
|
+
.attr('src', vMatch[0] + '/embed/simple')
|
|
1640
|
+
.attr('width', '600').attr('height', '600')
|
|
1641
|
+
.attr('class', 'vine-embed');
|
|
1642
|
+
} else if (vimMatch && vimMatch[3].length > 0) {
|
|
1643
|
+
$video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
|
|
1644
|
+
.attr('src', '//player.vimeo.com/video/' + vimMatch[3])
|
|
1645
|
+
.attr('width', '640').attr('height', '360');
|
|
1646
|
+
} else if (dmMatch && dmMatch[2].length > 0) {
|
|
1647
|
+
$video = $('<iframe>')
|
|
1648
|
+
.attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
|
|
1649
|
+
.attr('width', '640').attr('height', '360');
|
|
1650
|
+
} else {
|
|
1651
|
+
// this is not a known video link. Now what, Cat? Now what?
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
if ($video) {
|
|
1655
|
+
$video.attr('frameborder', 0);
|
|
1656
|
+
range.create().insertNode($video[0]);
|
|
1657
|
+
}
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* formatBlock
|
|
1662
|
+
*
|
|
1663
|
+
* @param {jQuery} $editable
|
|
1664
|
+
* @param {String} sTagName
|
|
1665
|
+
*/
|
|
1666
|
+
this.formatBlock = function ($editable, sTagName) {
|
|
1667
|
+
recordUndo($editable);
|
|
1668
|
+
sTagName = agent.bMSIE ? '<' + sTagName + '>' : sTagName;
|
|
1669
|
+
document.execCommand('FormatBlock', false, sTagName);
|
|
1670
|
+
};
|
|
1671
|
+
|
|
1672
|
+
this.formatPara = function ($editable) {
|
|
1673
|
+
this.formatBlock($editable, 'P');
|
|
1674
|
+
};
|
|
1675
|
+
|
|
1676
|
+
/* jshint ignore:start */
|
|
1677
|
+
for (var idx = 1; idx <= 6; idx ++) {
|
|
1678
|
+
this['formatH' + idx] = function (idx) {
|
|
1679
|
+
return function ($editable) {
|
|
1680
|
+
this.formatBlock($editable, 'H' + idx);
|
|
1681
|
+
};
|
|
1682
|
+
}(idx);
|
|
1683
|
+
};
|
|
1684
|
+
/* jshint ignore:end */
|
|
1685
|
+
|
|
1686
|
+
/**
|
|
1687
|
+
* fontsize
|
|
1688
|
+
* FIXME: Still buggy
|
|
1689
|
+
*
|
|
1690
|
+
* @param {jQuery} $editable
|
|
1691
|
+
* @param {String} sValue - px
|
|
1692
|
+
*/
|
|
1693
|
+
this.fontSize = function ($editable, sValue) {
|
|
1694
|
+
recordUndo($editable);
|
|
1695
|
+
document.execCommand('fontSize', false, 3);
|
|
1696
|
+
if (agent.bFF) {
|
|
1697
|
+
// firefox: <font size="3"> to <span style='font-size={sValue}px;'>, buggy
|
|
1698
|
+
$editable.find('font[size=3]').removeAttr('size').css('font-size', sValue + 'px');
|
|
1699
|
+
} else {
|
|
1700
|
+
// chrome: <span style="font-size: medium"> to <span style='font-size={sValue}px;'>
|
|
1701
|
+
$editable.find('span').filter(function () {
|
|
1702
|
+
return this.style.fontSize === 'medium';
|
|
1703
|
+
}).css('font-size', sValue + 'px');
|
|
1704
|
+
}
|
|
1705
|
+
};
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* lineHeight
|
|
1709
|
+
* @param {jQuery} $editable
|
|
1710
|
+
* @param {String} sValue
|
|
1711
|
+
*/
|
|
1712
|
+
this.lineHeight = function ($editable, sValue) {
|
|
1713
|
+
recordUndo($editable);
|
|
1714
|
+
style.stylePara(range.create(), {lineHeight: sValue});
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* unlink
|
|
1719
|
+
* @param {jQuery} $editable
|
|
1720
|
+
*/
|
|
1721
|
+
this.unlink = function ($editable) {
|
|
1722
|
+
var rng = range.create();
|
|
1723
|
+
if (rng.isOnAnchor()) {
|
|
1724
|
+
recordUndo($editable);
|
|
1725
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
|
1726
|
+
rng = range.createFromNode(elAnchor);
|
|
1727
|
+
rng.select();
|
|
1728
|
+
document.execCommand('unlink');
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
* create link
|
|
1734
|
+
*
|
|
1735
|
+
* @param {jQuery} $editable
|
|
1736
|
+
* @param {String} sLinkUrl
|
|
1737
|
+
* @param {Boolean} bNewWindow
|
|
1738
|
+
*/
|
|
1739
|
+
this.createLink = function ($editable, sLinkText, sLinkUrl, bNewWindow) {
|
|
1740
|
+
var rng = range.create();
|
|
1741
|
+
recordUndo($editable);
|
|
1742
|
+
|
|
1743
|
+
// protocol
|
|
1744
|
+
var sLinkUrlWithProtocol = sLinkUrl;
|
|
1745
|
+
if (sLinkUrl.indexOf('@') !== -1 && sLinkUrl.indexOf(':') === -1) {
|
|
1746
|
+
sLinkUrlWithProtocol = 'mailto:' + sLinkUrl;
|
|
1747
|
+
} else if (sLinkUrl.indexOf('://') === -1) {
|
|
1748
|
+
sLinkUrlWithProtocol = 'http://' + sLinkUrl;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// createLink when range collapsed (IE, Firefox).
|
|
1752
|
+
if ((agent.bMSIE || agent.bFF) && rng.isCollapsed()) {
|
|
1753
|
+
rng.insertNode($('<A id="linkAnchor">' + sLinkText + '</A>')[0]);
|
|
1754
|
+
var $anchor = $('#linkAnchor').attr('href', sLinkUrlWithProtocol).removeAttr('id');
|
|
1755
|
+
rng = range.createFromNode($anchor[0]);
|
|
1756
|
+
rng.select();
|
|
1757
|
+
} else {
|
|
1758
|
+
document.execCommand('createlink', false, sLinkUrlWithProtocol);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// target
|
|
1762
|
+
$.each(rng.nodes(dom.isAnchor), function (idx, elAnchor) {
|
|
1763
|
+
// update link text
|
|
1764
|
+
$(elAnchor).html(sLinkText);
|
|
1765
|
+
if (bNewWindow) {
|
|
1766
|
+
$(elAnchor).attr('target', '_blank');
|
|
1767
|
+
} else {
|
|
1768
|
+
$(elAnchor).removeAttr('target');
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
};
|
|
1772
|
+
|
|
1773
|
+
/**
|
|
1774
|
+
* get link info
|
|
1775
|
+
*
|
|
1776
|
+
* @return {Promise}
|
|
1777
|
+
*/
|
|
1778
|
+
this.getLinkInfo = function () {
|
|
1779
|
+
var rng = range.create();
|
|
1780
|
+
var bNewWindow = true;
|
|
1781
|
+
var sUrl = '';
|
|
1782
|
+
|
|
1783
|
+
// If range on anchor expand range on anchor(for edit link).
|
|
1784
|
+
if (rng.isOnAnchor()) {
|
|
1785
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
|
1786
|
+
rng = range.createFromNode(elAnchor);
|
|
1787
|
+
bNewWindow = $(elAnchor).attr('target') === '_blank';
|
|
1788
|
+
sUrl = elAnchor.href;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
return {
|
|
1792
|
+
text: rng.toString(),
|
|
1793
|
+
url: sUrl,
|
|
1794
|
+
newWindow: bNewWindow
|
|
1795
|
+
};
|
|
1796
|
+
};
|
|
1797
|
+
|
|
1798
|
+
/**
|
|
1799
|
+
* get video info
|
|
1800
|
+
*
|
|
1801
|
+
* @return {Object}
|
|
1802
|
+
*/
|
|
1803
|
+
this.getVideoInfo = function () {
|
|
1804
|
+
var rng = range.create();
|
|
1805
|
+
|
|
1806
|
+
if (rng.isOnAnchor()) {
|
|
1807
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
|
1808
|
+
rng = range.createFromNode(elAnchor);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
return {
|
|
1812
|
+
text: rng.toString()
|
|
1813
|
+
};
|
|
1814
|
+
};
|
|
1815
|
+
|
|
1816
|
+
this.color = function ($editable, sObjColor) {
|
|
1817
|
+
var oColor = JSON.parse(sObjColor);
|
|
1818
|
+
var foreColor = oColor.foreColor, backColor = oColor.backColor;
|
|
1819
|
+
|
|
1820
|
+
recordUndo($editable);
|
|
1821
|
+
if (foreColor) { document.execCommand('foreColor', false, foreColor); }
|
|
1822
|
+
if (backColor) { document.execCommand('backColor', false, backColor); }
|
|
1823
|
+
};
|
|
1824
|
+
|
|
1825
|
+
this.insertTable = function ($editable, sDim) {
|
|
1826
|
+
recordUndo($editable);
|
|
1827
|
+
var aDim = sDim.split('x');
|
|
1828
|
+
range.create().insertNode(table.createTable(aDim[0], aDim[1]));
|
|
1829
|
+
};
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* @param {jQuery} $editable
|
|
1833
|
+
* @param {String} sValue
|
|
1834
|
+
* @param {jQuery} $target
|
|
1835
|
+
*/
|
|
1836
|
+
this.floatMe = function ($editable, sValue, $target) {
|
|
1837
|
+
recordUndo($editable);
|
|
1838
|
+
$target.css('float', sValue);
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1841
|
+
/**
|
|
1842
|
+
* resize overlay element
|
|
1843
|
+
* @param {jQuery} $editable
|
|
1844
|
+
* @param {String} sValue
|
|
1845
|
+
* @param {jQuery} $target - target element
|
|
1846
|
+
*/
|
|
1847
|
+
this.resize = function ($editable, sValue, $target) {
|
|
1848
|
+
recordUndo($editable);
|
|
1849
|
+
|
|
1850
|
+
$target.css({
|
|
1851
|
+
width: $editable.width() * sValue + 'px',
|
|
1852
|
+
height: ''
|
|
1853
|
+
});
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
/**
|
|
1857
|
+
* @param {Position} pos
|
|
1858
|
+
* @param {jQuery} $target - target element
|
|
1859
|
+
* @param {Boolean} [bKeepRatio] - keep ratio
|
|
1860
|
+
*/
|
|
1861
|
+
this.resizeTo = function (pos, $target, bKeepRatio) {
|
|
1862
|
+
var szImage;
|
|
1863
|
+
if (bKeepRatio) {
|
|
1864
|
+
var newRatio = pos.y / pos.x;
|
|
1865
|
+
var ratio = $target.data('ratio');
|
|
1866
|
+
szImage = {
|
|
1867
|
+
width: ratio > newRatio ? pos.x : pos.y / ratio,
|
|
1868
|
+
height: ratio > newRatio ? pos.x * ratio : pos.y
|
|
1869
|
+
};
|
|
1870
|
+
} else {
|
|
1871
|
+
szImage = {
|
|
1872
|
+
width: pos.x,
|
|
1873
|
+
height: pos.y
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
$target.css(szImage);
|
|
1878
|
+
};
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* remove media object
|
|
1882
|
+
*
|
|
1883
|
+
* @param {jQuery} $editable
|
|
1884
|
+
* @param {String} sValue - dummy argument (for keep interface)
|
|
1885
|
+
* @param {jQuery} $target - target element
|
|
1886
|
+
*/
|
|
1887
|
+
this.removeMedia = function ($editable, sValue, $target) {
|
|
1888
|
+
recordUndo($editable);
|
|
1889
|
+
$target.detach();
|
|
1890
|
+
};
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* History
|
|
1895
|
+
* @class
|
|
1896
|
+
*/
|
|
1897
|
+
var History = function () {
|
|
1898
|
+
var aUndo = [], aRedo = [];
|
|
1899
|
+
|
|
1900
|
+
var makeSnap = function ($editable) {
|
|
1901
|
+
var elEditable = $editable[0], rng = range.create();
|
|
1902
|
+
return {
|
|
1903
|
+
contents: $editable.html(),
|
|
1904
|
+
bookmark: rng.bookmark(elEditable),
|
|
1905
|
+
scrollTop: $editable.scrollTop()
|
|
1906
|
+
};
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1909
|
+
var applySnap = function ($editable, oSnap) {
|
|
1910
|
+
$editable.html(oSnap.contents).scrollTop(oSnap.scrollTop);
|
|
1911
|
+
range.createFromBookmark($editable[0], oSnap.bookmark).select();
|
|
1912
|
+
};
|
|
1913
|
+
|
|
1914
|
+
this.undo = function ($editable) {
|
|
1915
|
+
var oSnap = makeSnap($editable);
|
|
1916
|
+
if (aUndo.length === 0) { return; }
|
|
1917
|
+
applySnap($editable, aUndo.pop());
|
|
1918
|
+
aRedo.push(oSnap);
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
this.redo = function ($editable) {
|
|
1922
|
+
var oSnap = makeSnap($editable);
|
|
1923
|
+
if (aRedo.length === 0) { return; }
|
|
1924
|
+
applySnap($editable, aRedo.pop());
|
|
1925
|
+
aUndo.push(oSnap);
|
|
1926
|
+
};
|
|
1927
|
+
|
|
1928
|
+
this.recordUndo = function ($editable) {
|
|
1929
|
+
aRedo = [];
|
|
1930
|
+
aUndo.push(makeSnap($editable));
|
|
1931
|
+
};
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
/**
|
|
1935
|
+
* Button
|
|
1936
|
+
*/
|
|
1937
|
+
var Button = function () {
|
|
1938
|
+
/**
|
|
1939
|
+
* update button status
|
|
1940
|
+
*
|
|
1941
|
+
* @param {jQuery} $container
|
|
1942
|
+
* @param {Object} oStyle
|
|
1943
|
+
*/
|
|
1944
|
+
this.update = function ($container, oStyle) {
|
|
1945
|
+
/**
|
|
1946
|
+
* handle dropdown's check mark (for fontname, fontsize, lineHeight).
|
|
1947
|
+
* @param {jQuery} $btn
|
|
1948
|
+
* @param {Number} nValue
|
|
1949
|
+
*/
|
|
1950
|
+
var checkDropdownMenu = function ($btn, nValue) {
|
|
1951
|
+
$btn.find('.dropdown-menu li a').each(function () {
|
|
1952
|
+
// always compare string to avoid creating another func.
|
|
1953
|
+
var bChecked = ($(this).data('value') + '') === (nValue + '');
|
|
1954
|
+
this.className = bChecked ? 'checked' : '';
|
|
1955
|
+
});
|
|
1956
|
+
};
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* update button state(active or not).
|
|
1960
|
+
*
|
|
1961
|
+
* @param {String} sSelector
|
|
1962
|
+
* @param {Function} pred
|
|
1963
|
+
*/
|
|
1964
|
+
var btnState = function (sSelector, pred) {
|
|
1965
|
+
var $btn = $container.find(sSelector);
|
|
1966
|
+
$btn.toggleClass('active', pred());
|
|
1967
|
+
};
|
|
1968
|
+
|
|
1969
|
+
// fontname
|
|
1970
|
+
var $fontname = $container.find('.note-fontname');
|
|
1971
|
+
if ($fontname.length > 0) {
|
|
1972
|
+
var selectedFont = oStyle['font-family'];
|
|
1973
|
+
if (!!selectedFont) {
|
|
1974
|
+
selectedFont = list.head(selectedFont.split(','));
|
|
1975
|
+
selectedFont = selectedFont.replace(/\'/g, '');
|
|
1976
|
+
$fontname.find('.note-current-fontname').text(selectedFont);
|
|
1977
|
+
checkDropdownMenu($fontname, selectedFont);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// fontsize
|
|
1982
|
+
var $fontsize = $container.find('.note-fontsize');
|
|
1983
|
+
$fontsize.find('.note-current-fontsize').text(oStyle['font-size']);
|
|
1984
|
+
checkDropdownMenu($fontsize, parseFloat(oStyle['font-size']));
|
|
1985
|
+
|
|
1986
|
+
// lineheight
|
|
1987
|
+
var $lineHeight = $container.find('.note-height');
|
|
1988
|
+
checkDropdownMenu($lineHeight, parseFloat(oStyle['line-height']));
|
|
1989
|
+
|
|
1990
|
+
btnState('button[data-event="bold"]', function () {
|
|
1991
|
+
return oStyle['font-bold'] === 'bold';
|
|
1992
|
+
});
|
|
1993
|
+
btnState('button[data-event="italic"]', function () {
|
|
1994
|
+
return oStyle['font-italic'] === 'italic';
|
|
1995
|
+
});
|
|
1996
|
+
btnState('button[data-event="underline"]', function () {
|
|
1997
|
+
return oStyle['font-underline'] === 'underline';
|
|
1998
|
+
});
|
|
1999
|
+
btnState('button[data-event="strikethrough"]', function () {
|
|
2000
|
+
return oStyle['font-strikethrough'] === 'strikethrough';
|
|
2001
|
+
});
|
|
2002
|
+
btnState('button[data-event="superscript"]', function () {
|
|
2003
|
+
return oStyle['font-superscript'] === 'superscript';
|
|
2004
|
+
});
|
|
2005
|
+
btnState('button[data-event="subscript"]', function () {
|
|
2006
|
+
return oStyle['font-subscript'] === 'subscript';
|
|
2007
|
+
});
|
|
2008
|
+
btnState('button[data-event="justifyLeft"]', function () {
|
|
2009
|
+
return oStyle['text-align'] === 'left' || oStyle['text-align'] === 'start';
|
|
2010
|
+
});
|
|
2011
|
+
btnState('button[data-event="justifyCenter"]', function () {
|
|
2012
|
+
return oStyle['text-align'] === 'center';
|
|
2013
|
+
});
|
|
2014
|
+
btnState('button[data-event="justifyRight"]', function () {
|
|
2015
|
+
return oStyle['text-align'] === 'right';
|
|
2016
|
+
});
|
|
2017
|
+
btnState('button[data-event="justifyFull"]', function () {
|
|
2018
|
+
return oStyle['text-align'] === 'justify';
|
|
2019
|
+
});
|
|
2020
|
+
btnState('button[data-event="insertUnorderedList"]', function () {
|
|
2021
|
+
return oStyle['list-style'] === 'unordered';
|
|
2022
|
+
});
|
|
2023
|
+
btnState('button[data-event="insertOrderedList"]', function () {
|
|
2024
|
+
return oStyle['list-style'] === 'ordered';
|
|
2025
|
+
});
|
|
2026
|
+
};
|
|
2027
|
+
|
|
2028
|
+
/**
|
|
2029
|
+
* update recent color
|
|
2030
|
+
*
|
|
2031
|
+
* @param {Element} elBtn
|
|
2032
|
+
* @param {String} sEvent
|
|
2033
|
+
* @param {sValue} sValue
|
|
2034
|
+
*/
|
|
2035
|
+
this.updateRecentColor = function (elBtn, sEvent, sValue) {
|
|
2036
|
+
var $color = $(elBtn).closest('.note-color');
|
|
2037
|
+
var $recentColor = $color.find('.note-recent-color');
|
|
2038
|
+
var oColor = JSON.parse($recentColor.attr('data-value'));
|
|
2039
|
+
oColor[sEvent] = sValue;
|
|
2040
|
+
$recentColor.attr('data-value', JSON.stringify(oColor));
|
|
2041
|
+
var sKey = sEvent === 'backColor' ? 'background-color' : 'color';
|
|
2042
|
+
$recentColor.find('i').css(sKey, sValue);
|
|
2043
|
+
};
|
|
2044
|
+
};
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* Toolbar
|
|
2048
|
+
*/
|
|
2049
|
+
var Toolbar = function () {
|
|
2050
|
+
var button = new Button();
|
|
2051
|
+
|
|
2052
|
+
this.update = function ($toolbar, oStyle) {
|
|
2053
|
+
button.update($toolbar, oStyle);
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
this.updateRecentColor = function (elBtn, sEvent, sValue) {
|
|
2057
|
+
button.updateRecentColor(elBtn, sEvent, sValue);
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
/**
|
|
2061
|
+
* activate buttons exclude codeview
|
|
2062
|
+
* @param {jQuery} $toolbar
|
|
2063
|
+
*/
|
|
2064
|
+
this.activate = function ($toolbar) {
|
|
2065
|
+
$toolbar.find('button').not('button[data-event="codeview"]').removeClass('disabled');
|
|
2066
|
+
};
|
|
2067
|
+
|
|
2068
|
+
/**
|
|
2069
|
+
* deactivate buttons exclude codeview
|
|
2070
|
+
* @param {jQuery} $toolbar
|
|
2071
|
+
*/
|
|
2072
|
+
this.deactivate = function ($toolbar) {
|
|
2073
|
+
$toolbar.find('button').not('button[data-event="codeview"]').addClass('disabled');
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
this.updateFullscreen = function ($container, bFullscreen) {
|
|
2077
|
+
var $btn = $container.find('button[data-event="fullscreen"]');
|
|
2078
|
+
$btn.toggleClass('active', bFullscreen);
|
|
2079
|
+
};
|
|
2080
|
+
|
|
2081
|
+
this.updateCodeview = function ($container, bCodeview) {
|
|
2082
|
+
var $btn = $container.find('button[data-event="codeview"]');
|
|
2083
|
+
$btn.toggleClass('active', bCodeview);
|
|
2084
|
+
};
|
|
2085
|
+
};
|
|
2086
|
+
|
|
2087
|
+
/**
|
|
2088
|
+
* Popover (http://getbootstrap.com/javascript/#popovers)
|
|
2089
|
+
*/
|
|
2090
|
+
var Popover = function () {
|
|
2091
|
+
var button = new Button();
|
|
2092
|
+
|
|
2093
|
+
/**
|
|
2094
|
+
* show popover
|
|
2095
|
+
* @param {jQuery} popover
|
|
2096
|
+
* @param {Element} elPlaceholder - placeholder for popover
|
|
2097
|
+
*/
|
|
2098
|
+
var showPopover = function ($popover, elPlaceholder) {
|
|
2099
|
+
var $placeholder = $(elPlaceholder);
|
|
2100
|
+
var pos = $placeholder.position();
|
|
2101
|
+
|
|
2102
|
+
// include margin
|
|
2103
|
+
var height = $placeholder.outerHeight(true);
|
|
2104
|
+
|
|
2105
|
+
// display popover below placeholder.
|
|
2106
|
+
$popover.css({
|
|
2107
|
+
display: 'block',
|
|
2108
|
+
left: pos.left,
|
|
2109
|
+
top: pos.top + height
|
|
2110
|
+
});
|
|
2111
|
+
};
|
|
2112
|
+
|
|
2113
|
+
var PX_POPOVER_ARROW_OFFSET_X = 20;
|
|
2114
|
+
|
|
2115
|
+
/**
|
|
2116
|
+
* update current state
|
|
2117
|
+
* @param {jQuery} $popover - popover container
|
|
2118
|
+
* @param {Object} oStyle - style object
|
|
2119
|
+
* @param {Boolean} isAirMode
|
|
2120
|
+
*/
|
|
2121
|
+
this.update = function ($popover, oStyle, isAirMode) {
|
|
2122
|
+
button.update($popover, oStyle);
|
|
2123
|
+
|
|
2124
|
+
var $linkPopover = $popover.find('.note-link-popover');
|
|
2125
|
+
|
|
2126
|
+
if (oStyle.anchor) {
|
|
2127
|
+
var $anchor = $linkPopover.find('a');
|
|
2128
|
+
$anchor.attr('href', oStyle.anchor.href).html(oStyle.anchor.href);
|
|
2129
|
+
showPopover($linkPopover, oStyle.anchor);
|
|
2130
|
+
} else {
|
|
2131
|
+
$linkPopover.hide();
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
var $imagePopover = $popover.find('.note-image-popover');
|
|
2135
|
+
if (oStyle.image) {
|
|
2136
|
+
showPopover($imagePopover, oStyle.image);
|
|
2137
|
+
} else {
|
|
2138
|
+
$imagePopover.hide();
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
if (isAirMode) {
|
|
2142
|
+
var $airPopover = $popover.find('.note-air-popover');
|
|
2143
|
+
if (!oStyle.range.isCollapsed()) {
|
|
2144
|
+
var bnd = func.rect2bnd(list.last(oStyle.range.getClientRects()));
|
|
2145
|
+
$airPopover.css({
|
|
2146
|
+
display: 'block',
|
|
2147
|
+
left: Math.max(bnd.left + bnd.width / 2 - PX_POPOVER_ARROW_OFFSET_X, 0),
|
|
2148
|
+
top: bnd.top + bnd.height
|
|
2149
|
+
});
|
|
2150
|
+
} else {
|
|
2151
|
+
$airPopover.hide();
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
|
|
2156
|
+
this.updateRecentColor = function (elBtn, sEvent, sValue) {
|
|
2157
|
+
button.updateRecentColor(elBtn, sEvent, sValue);
|
|
2158
|
+
};
|
|
2159
|
+
|
|
2160
|
+
/**
|
|
2161
|
+
* hide all popovers
|
|
2162
|
+
* @param {jQuery} $popover - popover contaienr
|
|
2163
|
+
*/
|
|
2164
|
+
this.hide = function ($popover) {
|
|
2165
|
+
$popover.children().hide();
|
|
2166
|
+
};
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
/**
|
|
2170
|
+
* Handle
|
|
2171
|
+
*/
|
|
2172
|
+
var Handle = function () {
|
|
2173
|
+
/**
|
|
2174
|
+
* update handle
|
|
2175
|
+
* @param {jQuery} $handle
|
|
2176
|
+
* @param {Object} oStyle
|
|
2177
|
+
*/
|
|
2178
|
+
this.update = function ($handle, oStyle) {
|
|
2179
|
+
var $selection = $handle.find('.note-control-selection');
|
|
2180
|
+
if (oStyle.image) {
|
|
2181
|
+
var $image = $(oStyle.image);
|
|
2182
|
+
var pos = $image.position();
|
|
2183
|
+
|
|
2184
|
+
// include margin
|
|
2185
|
+
var szImage = {
|
|
2186
|
+
w: $image.outerWidth(true),
|
|
2187
|
+
h: $image.outerHeight(true)
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
$selection.css({
|
|
2191
|
+
display: 'block',
|
|
2192
|
+
left: pos.left,
|
|
2193
|
+
top: pos.top,
|
|
2194
|
+
width: szImage.w,
|
|
2195
|
+
height: szImage.h
|
|
2196
|
+
}).data('target', oStyle.image); // save current image element.
|
|
2197
|
+
var sSizing = szImage.w + 'x' + szImage.h;
|
|
2198
|
+
$selection.find('.note-control-selection-info').text(sSizing);
|
|
2199
|
+
} else {
|
|
2200
|
+
$selection.hide();
|
|
2201
|
+
}
|
|
2202
|
+
};
|
|
2203
|
+
|
|
2204
|
+
this.hide = function ($handle) {
|
|
2205
|
+
$handle.children().hide();
|
|
2206
|
+
};
|
|
2207
|
+
};
|
|
2208
|
+
|
|
2209
|
+
/**
|
|
2210
|
+
* Dialog
|
|
2211
|
+
*
|
|
2212
|
+
* @class
|
|
2213
|
+
*/
|
|
2214
|
+
var Dialog = function () {
|
|
2215
|
+
|
|
2216
|
+
/**
|
|
2217
|
+
* toggle button status
|
|
2218
|
+
*
|
|
2219
|
+
* @param {jQuery} $btn
|
|
2220
|
+
* @param {Boolean} bEnable
|
|
2221
|
+
*/
|
|
2222
|
+
var toggleBtn = function ($btn, bEnable) {
|
|
2223
|
+
$btn.toggleClass('disabled', !bEnable);
|
|
2224
|
+
$btn.attr('disabled', !bEnable);
|
|
2225
|
+
};
|
|
2226
|
+
|
|
2227
|
+
/**
|
|
2228
|
+
* show image dialog
|
|
2229
|
+
*
|
|
2230
|
+
* @param {jQuery} $editable
|
|
2231
|
+
* @param {jQuery} $dialog
|
|
2232
|
+
* @return {Promise}
|
|
2233
|
+
*/
|
|
2234
|
+
this.showImageDialog = function ($editable, $dialog) {
|
|
2235
|
+
return $.Deferred(function (deferred) {
|
|
2236
|
+
var $imageDialog = $dialog.find('.note-image-dialog');
|
|
2237
|
+
|
|
2238
|
+
var $imageInput = $dialog.find('.note-image-input'),
|
|
2239
|
+
$imageUrl = $dialog.find('.note-image-url'),
|
|
2240
|
+
$imageBtn = $dialog.find('.note-image-btn');
|
|
2241
|
+
|
|
2242
|
+
$imageDialog.one('shown.bs.modal', function () {
|
|
2243
|
+
// Cloning imageInput to clear element.
|
|
2244
|
+
$imageInput.replaceWith($imageInput.clone()
|
|
2245
|
+
.on('change', function () {
|
|
2246
|
+
$imageDialog.modal('hide');
|
|
2247
|
+
deferred.resolve(this.files);
|
|
2248
|
+
})
|
|
2249
|
+
);
|
|
2250
|
+
|
|
2251
|
+
$imageBtn.click(function (event) {
|
|
2252
|
+
event.preventDefault();
|
|
2253
|
+
|
|
2254
|
+
$imageDialog.modal('hide');
|
|
2255
|
+
deferred.resolve($imageUrl.val());
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2258
|
+
$imageUrl.keyup(function () {
|
|
2259
|
+
toggleBtn($imageBtn, $imageUrl.val());
|
|
2260
|
+
}).val('').focus();
|
|
2261
|
+
}).one('hidden.bs.modal', function () {
|
|
2262
|
+
$editable.focus();
|
|
2263
|
+
$imageInput.off('change');
|
|
2264
|
+
$imageUrl.off('keyup');
|
|
2265
|
+
$imageBtn.off('click');
|
|
2266
|
+
}).modal('show');
|
|
2267
|
+
});
|
|
2268
|
+
};
|
|
2269
|
+
|
|
2270
|
+
/**
|
|
2271
|
+
* Show video dialog and set event handlers on dialog controls.
|
|
2272
|
+
*
|
|
2273
|
+
* @param {jQuery} $dialog
|
|
2274
|
+
* @param {Object} videoInfo
|
|
2275
|
+
* @return {Promise}
|
|
2276
|
+
*/
|
|
2277
|
+
this.showVideoDialog = function ($editable, $dialog, videoInfo) {
|
|
2278
|
+
return $.Deferred(function (deferred) {
|
|
2279
|
+
var $videoDialog = $dialog.find('.note-video-dialog');
|
|
2280
|
+
var $videoUrl = $videoDialog.find('.note-video-url'),
|
|
2281
|
+
$videoBtn = $videoDialog.find('.note-video-btn');
|
|
2282
|
+
|
|
2283
|
+
$videoDialog.one('shown.bs.modal', function () {
|
|
2284
|
+
$videoUrl.val(videoInfo.text).keyup(function () {
|
|
2285
|
+
toggleBtn($videoBtn, $videoUrl.val());
|
|
2286
|
+
}).trigger('keyup').trigger('focus');
|
|
2287
|
+
|
|
2288
|
+
$videoBtn.click(function (event) {
|
|
2289
|
+
event.preventDefault();
|
|
2290
|
+
|
|
2291
|
+
$videoDialog.modal('hide');
|
|
2292
|
+
deferred.resolve($videoUrl.val());
|
|
2293
|
+
});
|
|
2294
|
+
}).one('hidden.bs.modal', function () {
|
|
2295
|
+
$editable.focus();
|
|
2296
|
+
$videoUrl.off('keyup');
|
|
2297
|
+
$videoBtn.off('click');
|
|
2298
|
+
}).modal('show');
|
|
2299
|
+
});
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
/**
|
|
2303
|
+
* Show link dialog and set event handlers on dialog controls.
|
|
2304
|
+
*
|
|
2305
|
+
* @param {jQuery} $dialog
|
|
2306
|
+
* @param {Object} linkInfo
|
|
2307
|
+
* @return {Promise}
|
|
2308
|
+
*/
|
|
2309
|
+
this.showLinkDialog = function ($editable, $dialog, linkInfo) {
|
|
2310
|
+
return $.Deferred(function (deferred) {
|
|
2311
|
+
var $linkDialog = $dialog.find('.note-link-dialog');
|
|
2312
|
+
|
|
2313
|
+
var $linkText = $linkDialog.find('.note-link-text'),
|
|
2314
|
+
$linkUrl = $linkDialog.find('.note-link-url'),
|
|
2315
|
+
$linkBtn = $linkDialog.find('.note-link-btn'),
|
|
2316
|
+
$openInNewWindow = $linkDialog.find('input[type=checkbox]');
|
|
2317
|
+
|
|
2318
|
+
$linkDialog.one('shown.bs.modal', function () {
|
|
2319
|
+
$linkText.val(linkInfo.text);
|
|
2320
|
+
|
|
2321
|
+
$linkText.keyup(function () {
|
|
2322
|
+
// if linktext was modified by keyup,
|
|
2323
|
+
// stop cloning text from linkUrl
|
|
2324
|
+
linkInfo.text = $linkText.val();
|
|
2325
|
+
});
|
|
2326
|
+
|
|
2327
|
+
// if no url was given, copy text to url
|
|
2328
|
+
if (!linkInfo.url) {
|
|
2329
|
+
linkInfo.url = linkInfo.text;
|
|
2330
|
+
toggleBtn($linkBtn, linkInfo.text);
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
$linkUrl.keyup(function () {
|
|
2334
|
+
toggleBtn($linkBtn, $linkUrl.val());
|
|
2335
|
+
// display same link on `Text to display` input
|
|
2336
|
+
// when create a new link
|
|
2337
|
+
if (!linkInfo.text) {
|
|
2338
|
+
$linkText.val($linkUrl.val());
|
|
2339
|
+
}
|
|
2340
|
+
}).val(linkInfo.url).trigger('focus').trigger('select');
|
|
2341
|
+
|
|
2342
|
+
$openInNewWindow.prop('checked', linkInfo.newWindow);
|
|
2343
|
+
|
|
2344
|
+
$linkBtn.one('click', function (event) {
|
|
2345
|
+
event.preventDefault();
|
|
2346
|
+
|
|
2347
|
+
$linkDialog.modal('hide');
|
|
2348
|
+
deferred.resolve($linkText.val(), $linkUrl.val(), $openInNewWindow.is(':checked'));
|
|
2349
|
+
});
|
|
2350
|
+
}).one('hidden.bs.modal', function () {
|
|
2351
|
+
$editable.focus();
|
|
2352
|
+
$linkUrl.off('keyup');
|
|
2353
|
+
}).modal('show');
|
|
2354
|
+
}).promise();
|
|
2355
|
+
};
|
|
2356
|
+
|
|
2357
|
+
/**
|
|
2358
|
+
* show help dialog
|
|
2359
|
+
*
|
|
2360
|
+
* @param {jQuery} $dialog
|
|
2361
|
+
*/
|
|
2362
|
+
this.showHelpDialog = function ($editable, $dialog) {
|
|
2363
|
+
var $helpDialog = $dialog.find('.note-help-dialog');
|
|
2364
|
+
|
|
2365
|
+
$helpDialog.one('hidden.bs.modal', function () {
|
|
2366
|
+
$editable.focus();
|
|
2367
|
+
}).modal('show');
|
|
2368
|
+
};
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
/**
|
|
2372
|
+
* EventHandler
|
|
2373
|
+
*/
|
|
2374
|
+
var EventHandler = function () {
|
|
2375
|
+
var $document = $(document);
|
|
2376
|
+
|
|
2377
|
+
var editor = new Editor();
|
|
2378
|
+
var toolbar = new Toolbar(), popover = new Popover();
|
|
2379
|
+
var handle = new Handle(), dialog = new Dialog();
|
|
2380
|
+
|
|
2381
|
+
/**
|
|
2382
|
+
* returns makeLayoutInfo from editor's descendant node.
|
|
2383
|
+
*
|
|
2384
|
+
* @param {Element} descendant
|
|
2385
|
+
* @returns {Object}
|
|
2386
|
+
*/
|
|
2387
|
+
var makeLayoutInfo = function (descendant) {
|
|
2388
|
+
var $target = $(descendant).closest('.note-editor, .note-air-editor, .note-air-layout');
|
|
2389
|
+
|
|
2390
|
+
if ($target.length === 0) { return null; }
|
|
2391
|
+
|
|
2392
|
+
var $editor;
|
|
2393
|
+
if ($target.is('.note-editor, .note-air-editor')) {
|
|
2394
|
+
$editor = $target;
|
|
2395
|
+
} else {
|
|
2396
|
+
$editor = $('#note-editor-' + list.last($target.attr('id').split('-')));
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
return dom.buildLayoutInfo($editor);
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
/**
|
|
2403
|
+
* insert Images from file array.
|
|
2404
|
+
*
|
|
2405
|
+
* @param {jQuery} $editable
|
|
2406
|
+
* @param {File[]} files
|
|
2407
|
+
*/
|
|
2408
|
+
var insertImages = function ($editable, files) {
|
|
2409
|
+
editor.restoreRange($editable);
|
|
2410
|
+
var callbacks = $editable.data('callbacks');
|
|
2411
|
+
|
|
2412
|
+
// If onImageUpload options setted
|
|
2413
|
+
if (callbacks.onImageUpload) {
|
|
2414
|
+
callbacks.onImageUpload(files, editor, $editable);
|
|
2415
|
+
// else insert Image as dataURL
|
|
2416
|
+
} else {
|
|
2417
|
+
$.each(files, function (idx, file) {
|
|
2418
|
+
async.readFileAsDataURL(file).then(function (sDataURL) {
|
|
2419
|
+
editor.insertImage($editable, sDataURL);
|
|
2420
|
+
}).fail(function () {
|
|
2421
|
+
if (callbacks.onImageUploadError) {
|
|
2422
|
+
callbacks.onImageUploadError();
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
|
|
2429
|
+
var hMousedown = function (event) {
|
|
2430
|
+
//preventDefault Selection for FF, IE8+
|
|
2431
|
+
if (dom.isImg(event.target)) {
|
|
2432
|
+
event.preventDefault();
|
|
2433
|
+
}
|
|
2434
|
+
};
|
|
2435
|
+
|
|
2436
|
+
var hToolbarAndPopoverUpdate = function (event) {
|
|
2437
|
+
// delay for range after mouseup
|
|
2438
|
+
setTimeout(function () {
|
|
2439
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
|
2440
|
+
var oStyle = editor.currentStyle(event.target);
|
|
2441
|
+
if (!oStyle) { return; }
|
|
2442
|
+
|
|
2443
|
+
var isAirMode = oLayoutInfo.editor().data('options').airMode;
|
|
2444
|
+
if (!isAirMode) {
|
|
2445
|
+
toolbar.update(oLayoutInfo.toolbar(), oStyle);
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
popover.update(oLayoutInfo.popover(), oStyle, isAirMode);
|
|
2449
|
+
handle.update(oLayoutInfo.handle(), oStyle);
|
|
2450
|
+
}, 0);
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
var hScroll = function (event) {
|
|
2454
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
|
2455
|
+
//hide popover and handle when scrolled
|
|
2456
|
+
popover.hide(oLayoutInfo.popover());
|
|
2457
|
+
handle.hide(oLayoutInfo.handle());
|
|
2458
|
+
};
|
|
2459
|
+
|
|
2460
|
+
/**
|
|
2461
|
+
* paste clipboard image
|
|
2462
|
+
*
|
|
2463
|
+
* @param {Event} event
|
|
2464
|
+
*/
|
|
2465
|
+
var hPasteClipboardImage = function (event) {
|
|
2466
|
+
var originalEvent = event.originalEvent;
|
|
2467
|
+
if (!originalEvent.clipboardData ||
|
|
2468
|
+
!originalEvent.clipboardData.items ||
|
|
2469
|
+
!originalEvent.clipboardData.items.length) {
|
|
2470
|
+
return;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
|
2474
|
+
var item = list.head(originalEvent.clipboardData.items);
|
|
2475
|
+
var bClipboardImage = item.kind === 'file' && item.type.indexOf('image/') !== -1;
|
|
2476
|
+
|
|
2477
|
+
if (bClipboardImage) {
|
|
2478
|
+
insertImages(oLayoutInfo.editable(), [item.getAsFile()]);
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
|
|
2482
|
+
/**
|
|
2483
|
+
* `mousedown` event handler on $handle
|
|
2484
|
+
* - controlSizing: resize image
|
|
2485
|
+
*
|
|
2486
|
+
* @param {MouseEvent} event
|
|
2487
|
+
*/
|
|
2488
|
+
var hHandleMousedown = function (event) {
|
|
2489
|
+
if (dom.isControlSizing(event.target)) {
|
|
2490
|
+
event.preventDefault();
|
|
2491
|
+
event.stopPropagation();
|
|
2492
|
+
|
|
2493
|
+
var oLayoutInfo = makeLayoutInfo(event.target),
|
|
2494
|
+
$handle = oLayoutInfo.handle(), $popover = oLayoutInfo.popover(),
|
|
2495
|
+
$editable = oLayoutInfo.editable();
|
|
2496
|
+
|
|
2497
|
+
var elTarget = $handle.find('.note-control-selection').data('target'),
|
|
2498
|
+
$target = $(elTarget), posStart = $target.offset(),
|
|
2499
|
+
scrollTop = $document.scrollTop();
|
|
2500
|
+
|
|
2501
|
+
$document.on('mousemove', function (event) {
|
|
2502
|
+
editor.resizeTo({
|
|
2503
|
+
x: event.clientX - posStart.left,
|
|
2504
|
+
y: event.clientY - (posStart.top - scrollTop)
|
|
2505
|
+
}, $target, !event.shiftKey);
|
|
2506
|
+
|
|
2507
|
+
handle.update($handle, {image: elTarget});
|
|
2508
|
+
popover.update($popover, {image: elTarget});
|
|
2509
|
+
}).one('mouseup', function () {
|
|
2510
|
+
$document.off('mousemove');
|
|
2511
|
+
});
|
|
2512
|
+
|
|
2513
|
+
if (!$target.data('ratio')) { // original ratio.
|
|
2514
|
+
$target.data('ratio', $target.height() / $target.width());
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
editor.recordUndo($editable);
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
|
|
2521
|
+
var hToolbarAndPopoverMousedown = function (event) {
|
|
2522
|
+
// prevent default event when insertTable (FF, Webkit)
|
|
2523
|
+
var $btn = $(event.target).closest('[data-event]');
|
|
2524
|
+
if ($btn.length > 0) {
|
|
2525
|
+
event.preventDefault();
|
|
2526
|
+
}
|
|
2527
|
+
};
|
|
2528
|
+
|
|
2529
|
+
var toggleFullscreen = function (oLayoutInfo) {
|
|
2530
|
+
var $editor = oLayoutInfo.editor(),
|
|
2531
|
+
$toolbar = oLayoutInfo.toolbar(),
|
|
2532
|
+
$editable = oLayoutInfo.editable(),
|
|
2533
|
+
$codable = oLayoutInfo.codable();
|
|
2534
|
+
|
|
2535
|
+
var options = $editor.data('options');
|
|
2536
|
+
|
|
2537
|
+
var $scrollbar = $('html, body');
|
|
2538
|
+
|
|
2539
|
+
var resize = function (size) {
|
|
2540
|
+
$editor.css('width', size.w);
|
|
2541
|
+
$editable.css('height', size.h);
|
|
2542
|
+
$codable.css('height', size.h);
|
|
2543
|
+
if ($codable.data('cmEditor')) {
|
|
2544
|
+
$codable.data('cmEditor').setSize(null, size.h);
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
$editor.toggleClass('fullscreen');
|
|
2549
|
+
var isFullscreen = $editor.hasClass('fullscreen');
|
|
2550
|
+
if (isFullscreen) {
|
|
2551
|
+
$editable.data('orgHeight', $editable.css('height'));
|
|
2552
|
+
|
|
2553
|
+
$(window).on('resize', function () {
|
|
2554
|
+
resize({
|
|
2555
|
+
w: $(window).width(),
|
|
2556
|
+
h: $(window).height() - $toolbar.outerHeight()
|
|
2557
|
+
});
|
|
2558
|
+
}).trigger('resize');
|
|
2559
|
+
|
|
2560
|
+
$scrollbar.css('overflow', 'hidden');
|
|
2561
|
+
} else {
|
|
2562
|
+
$(window).off('resize');
|
|
2563
|
+
resize({
|
|
2564
|
+
w: options.width || '',
|
|
2565
|
+
h: $editable.data('orgHeight')
|
|
2566
|
+
});
|
|
2567
|
+
$scrollbar.css('overflow', 'visible');
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
toolbar.updateFullscreen($toolbar, isFullscreen);
|
|
2571
|
+
};
|
|
2572
|
+
|
|
2573
|
+
var toggleCodeView = function (oLayoutInfo) {
|
|
2574
|
+
var $editor = oLayoutInfo.editor(),
|
|
2575
|
+
$toolbar = oLayoutInfo.toolbar(),
|
|
2576
|
+
$editable = oLayoutInfo.editable(),
|
|
2577
|
+
$codable = oLayoutInfo.codable(),
|
|
2578
|
+
$popover = oLayoutInfo.popover();
|
|
2579
|
+
|
|
2580
|
+
var options = $editor.data('options');
|
|
2581
|
+
|
|
2582
|
+
var cmEditor, server;
|
|
2583
|
+
|
|
2584
|
+
$editor.toggleClass('codeview');
|
|
2585
|
+
|
|
2586
|
+
var bCodeview = $editor.hasClass('codeview');
|
|
2587
|
+
if (bCodeview) {
|
|
2588
|
+
$codable.val($editable.html());
|
|
2589
|
+
$codable.height($editable.height());
|
|
2590
|
+
toolbar.deactivate($toolbar);
|
|
2591
|
+
popover.hide($popover);
|
|
2592
|
+
$codable.focus();
|
|
2593
|
+
|
|
2594
|
+
// activate CodeMirror as codable
|
|
2595
|
+
if (agent.bCodeMirror) {
|
|
2596
|
+
cmEditor = CodeMirror.fromTextArea($codable[0], options.codemirror);
|
|
2597
|
+
|
|
2598
|
+
// CodeMirror TernServer
|
|
2599
|
+
if (options.codemirror.tern) {
|
|
2600
|
+
server = new CodeMirror.TernServer(options.codemirror.tern);
|
|
2601
|
+
cmEditor.ternServer = server;
|
|
2602
|
+
cmEditor.on('cursorActivity', function (cm) {
|
|
2603
|
+
server.updateArgHints(cm);
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// CodeMirror hasn't Padding.
|
|
2608
|
+
cmEditor.setSize(null, $editable.outerHeight());
|
|
2609
|
+
// autoFormatRange If formatting included
|
|
2610
|
+
if (cmEditor.autoFormatRange) {
|
|
2611
|
+
cmEditor.autoFormatRange({line: 0, ch: 0}, {
|
|
2612
|
+
line: cmEditor.lineCount(),
|
|
2613
|
+
ch: cmEditor.getTextArea().value.length
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
$codable.data('cmEditor', cmEditor);
|
|
2617
|
+
}
|
|
2618
|
+
} else {
|
|
2619
|
+
// deactivate CodeMirror as codable
|
|
2620
|
+
if (agent.bCodeMirror) {
|
|
2621
|
+
cmEditor = $codable.data('cmEditor');
|
|
2622
|
+
$codable.val(cmEditor.getValue());
|
|
2623
|
+
cmEditor.toTextArea();
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
$editable.html($codable.val() || dom.emptyPara);
|
|
2627
|
+
$editable.height(options.height ? $codable.height() : 'auto');
|
|
2628
|
+
|
|
2629
|
+
toolbar.activate($toolbar);
|
|
2630
|
+
$editable.focus();
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
toolbar.updateCodeview(oLayoutInfo.toolbar(), bCodeview);
|
|
2634
|
+
};
|
|
2635
|
+
|
|
2636
|
+
var hToolbarAndPopoverClick = function (event) {
|
|
2637
|
+
var $btn = $(event.target).closest('[data-event]');
|
|
2638
|
+
|
|
2639
|
+
if ($btn.length > 0) {
|
|
2640
|
+
var sEvent = $btn.attr('data-event'), sValue = $btn.attr('data-value');
|
|
2641
|
+
|
|
2642
|
+
var oLayoutInfo = makeLayoutInfo(event.target);
|
|
2643
|
+
var $dialog = oLayoutInfo.dialog(),
|
|
2644
|
+
$editable = oLayoutInfo.editable();
|
|
2645
|
+
|
|
2646
|
+
// before command: detect control selection element($target)
|
|
2647
|
+
var $target;
|
|
2648
|
+
if ($.inArray(sEvent, ['resize', 'floatMe', 'removeMedia']) !== -1) {
|
|
2649
|
+
var $selection = oLayoutInfo.handle().find('.note-control-selection');
|
|
2650
|
+
$target = $($selection.data('target'));
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
if (editor[sEvent]) { // on command
|
|
2654
|
+
$editable.trigger('focus');
|
|
2655
|
+
editor[sEvent]($editable, sValue, $target);
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// after command
|
|
2659
|
+
if ($.inArray(sEvent, ['backColor', 'foreColor']) !== -1) {
|
|
2660
|
+
var options = oLayoutInfo.editor().data('options', options);
|
|
2661
|
+
var module = options.airMode ? popover : toolbar;
|
|
2662
|
+
module.updateRecentColor(list.head($btn), sEvent, sValue);
|
|
2663
|
+
} else if (sEvent === 'showLinkDialog') { // popover to dialog
|
|
2664
|
+
$editable.focus();
|
|
2665
|
+
var linkInfo = editor.getLinkInfo();
|
|
2666
|
+
|
|
2667
|
+
editor.saveRange($editable);
|
|
2668
|
+
dialog.showLinkDialog($editable, $dialog, linkInfo).then(function (sLinkText, sLinkUrl, bNewWindow) {
|
|
2669
|
+
editor.restoreRange($editable);
|
|
2670
|
+
editor.createLink($editable, sLinkText, sLinkUrl, bNewWindow);
|
|
2671
|
+
// hide popover after creating link
|
|
2672
|
+
popover.hide(oLayoutInfo.popover());
|
|
2673
|
+
});
|
|
2674
|
+
} else if (sEvent === 'showImageDialog') {
|
|
2675
|
+
$editable.focus();
|
|
2676
|
+
|
|
2677
|
+
dialog.showImageDialog($editable, $dialog).then(function (data) {
|
|
2678
|
+
if (typeof data === 'string') {
|
|
2679
|
+
editor.restoreRange($editable);
|
|
2680
|
+
editor.insertImage($editable, data);
|
|
2681
|
+
} else {
|
|
2682
|
+
insertImages($editable, data);
|
|
2683
|
+
}
|
|
2684
|
+
});
|
|
2685
|
+
} else if (sEvent === 'showVideoDialog') {
|
|
2686
|
+
$editable.focus();
|
|
2687
|
+
var videoInfo = editor.getVideoInfo();
|
|
2688
|
+
|
|
2689
|
+
editor.saveRange($editable);
|
|
2690
|
+
dialog.showVideoDialog($editable, $dialog, videoInfo).then(function (sUrl) {
|
|
2691
|
+
editor.restoreRange($editable);
|
|
2692
|
+
editor.insertVideo($editable, sUrl);
|
|
2693
|
+
});
|
|
2694
|
+
} else if (sEvent === 'showHelpDialog') {
|
|
2695
|
+
dialog.showHelpDialog($editable, $dialog);
|
|
2696
|
+
} else if (sEvent === 'fullscreen') {
|
|
2697
|
+
toggleFullscreen(oLayoutInfo);
|
|
2698
|
+
} else if (sEvent === 'codeview') {
|
|
2699
|
+
toggleCodeView(oLayoutInfo);
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
hToolbarAndPopoverUpdate(event);
|
|
2703
|
+
}
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
var EDITABLE_PADDING = 24;
|
|
2707
|
+
/**
|
|
2708
|
+
* `mousedown` event handler on statusbar
|
|
2709
|
+
*
|
|
2710
|
+
* @param {MouseEvent} event
|
|
2711
|
+
*/
|
|
2712
|
+
var hStatusbarMousedown = function (event) {
|
|
2713
|
+
event.preventDefault();
|
|
2714
|
+
event.stopPropagation();
|
|
2715
|
+
|
|
2716
|
+
var $editable = makeLayoutInfo(event.target).editable();
|
|
2717
|
+
var nEditableTop = $editable.offset().top - $document.scrollTop();
|
|
2718
|
+
|
|
2719
|
+
$document.on('mousemove', function (event) {
|
|
2720
|
+
var nHeight = event.clientY - (nEditableTop + EDITABLE_PADDING);
|
|
2721
|
+
$editable.height(nHeight);
|
|
2722
|
+
}).one('mouseup', function () {
|
|
2723
|
+
$document.off('mousemove');
|
|
2724
|
+
});
|
|
2725
|
+
};
|
|
2726
|
+
|
|
2727
|
+
var PX_PER_EM = 18;
|
|
2728
|
+
var hDimensionPickerMove = function (event) {
|
|
2729
|
+
var $picker = $(event.target.parentNode); // target is mousecatcher
|
|
2730
|
+
var $dimensionDisplay = $picker.next();
|
|
2731
|
+
var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
|
|
2732
|
+
var $highlighted = $picker.find('.note-dimension-picker-highlighted');
|
|
2733
|
+
var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
|
|
2734
|
+
|
|
2735
|
+
var posOffset;
|
|
2736
|
+
// HTML5 with jQuery - e.offsetX is undefined in Firefox
|
|
2737
|
+
if (event.offsetX === undefined) {
|
|
2738
|
+
var posCatcher = $(event.target).offset();
|
|
2739
|
+
posOffset = {
|
|
2740
|
+
x: event.pageX - posCatcher.left,
|
|
2741
|
+
y: event.pageY - posCatcher.top
|
|
2742
|
+
};
|
|
2743
|
+
} else {
|
|
2744
|
+
posOffset = {
|
|
2745
|
+
x: event.offsetX,
|
|
2746
|
+
y: event.offsetY
|
|
2747
|
+
};
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
var dim = {
|
|
2751
|
+
c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
|
|
2752
|
+
r: Math.ceil(posOffset.y / PX_PER_EM) || 1
|
|
2753
|
+
};
|
|
2754
|
+
|
|
2755
|
+
$highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
|
|
2756
|
+
$catcher.attr('data-value', dim.c + 'x' + dim.r);
|
|
2757
|
+
|
|
2758
|
+
if (3 < dim.c && dim.c < 10) { // 5~10
|
|
2759
|
+
$unhighlighted.css({ width: dim.c + 1 + 'em'});
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
if (3 < dim.r && dim.r < 10) { // 5~10
|
|
2763
|
+
$unhighlighted.css({ height: dim.r + 1 + 'em'});
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
$dimensionDisplay.html(dim.c + ' x ' + dim.r);
|
|
2767
|
+
};
|
|
2768
|
+
|
|
2769
|
+
/**
|
|
2770
|
+
* attach Drag and Drop Events
|
|
2771
|
+
*
|
|
2772
|
+
* @param {Object} oLayoutInfo - layout Informations
|
|
2773
|
+
*/
|
|
2774
|
+
var attachDragAndDropEvent = function (oLayoutInfo) {
|
|
2775
|
+
var collection = $(), $dropzone = oLayoutInfo.dropzone,
|
|
2776
|
+
$dropzoneMessage = oLayoutInfo.dropzone.find('.note-dropzone-message');
|
|
2777
|
+
|
|
2778
|
+
// show dropzone on dragenter when dragging a object to document.
|
|
2779
|
+
$document.on('dragenter', function (e) {
|
|
2780
|
+
var bCodeview = oLayoutInfo.editor.hasClass('codeview');
|
|
2781
|
+
if (!bCodeview && collection.length === 0) {
|
|
2782
|
+
oLayoutInfo.editor.addClass('dragover');
|
|
2783
|
+
$dropzone.width(oLayoutInfo.editor.width());
|
|
2784
|
+
$dropzone.height(oLayoutInfo.editor.height());
|
|
2785
|
+
$dropzoneMessage.text('Drag Image Here');
|
|
2786
|
+
}
|
|
2787
|
+
collection = collection.add(e.target);
|
|
2788
|
+
}).on('dragleave', function (e) {
|
|
2789
|
+
collection = collection.not(e.target);
|
|
2790
|
+
if (collection.length === 0) {
|
|
2791
|
+
oLayoutInfo.editor.removeClass('dragover');
|
|
2792
|
+
}
|
|
2793
|
+
}).on('drop', function () {
|
|
2794
|
+
collection = $();
|
|
2795
|
+
oLayoutInfo.editor.removeClass('dragover');
|
|
2796
|
+
});
|
|
2797
|
+
|
|
2798
|
+
// change dropzone's message on hover.
|
|
2799
|
+
$dropzone.on('dragenter', function () {
|
|
2800
|
+
$dropzone.addClass('hover');
|
|
2801
|
+
$dropzoneMessage.text('Drop Image');
|
|
2802
|
+
}).on('dragleave', function () {
|
|
2803
|
+
$dropzone.removeClass('hover');
|
|
2804
|
+
$dropzoneMessage.text('Drag Image Here');
|
|
2805
|
+
});
|
|
2806
|
+
|
|
2807
|
+
// attach dropImage
|
|
2808
|
+
$dropzone.on('drop', function (event) {
|
|
2809
|
+
event.preventDefault();
|
|
2810
|
+
|
|
2811
|
+
var dataTransfer = event.originalEvent.dataTransfer;
|
|
2812
|
+
if (dataTransfer && dataTransfer.files) {
|
|
2813
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
|
2814
|
+
oLayoutInfo.editable().focus();
|
|
2815
|
+
insertImages(oLayoutInfo.editable(), dataTransfer.files);
|
|
2816
|
+
}
|
|
2817
|
+
}).on('dragover', false); // prevent default dragover event
|
|
2818
|
+
};
|
|
2819
|
+
|
|
2820
|
+
|
|
2821
|
+
/**
|
|
2822
|
+
* bind KeyMap on keydown
|
|
2823
|
+
*
|
|
2824
|
+
* @param {Object} oLayoutInfo
|
|
2825
|
+
* @param {Object} keyMap
|
|
2826
|
+
*/
|
|
2827
|
+
this.bindKeyMap = function (oLayoutInfo, keyMap) {
|
|
2828
|
+
var $editor = oLayoutInfo.editor;
|
|
2829
|
+
var $editable = oLayoutInfo.editable;
|
|
2830
|
+
|
|
2831
|
+
$editable.on('keydown', function (event) {
|
|
2832
|
+
var aKey = [];
|
|
2833
|
+
|
|
2834
|
+
// modifier
|
|
2835
|
+
if (event.metaKey) { aKey.push('CMD'); }
|
|
2836
|
+
if (event.ctrlKey) { aKey.push('CTRL'); }
|
|
2837
|
+
if (event.shiftKey) { aKey.push('SHIFT'); }
|
|
2838
|
+
|
|
2839
|
+
// keycode
|
|
2840
|
+
var keyName = key.nameFromCode[event.keyCode];
|
|
2841
|
+
if (keyName) { aKey.push(keyName); }
|
|
2842
|
+
|
|
2843
|
+
var handler = keyMap[aKey.join('+')];
|
|
2844
|
+
if (handler) {
|
|
2845
|
+
event.preventDefault();
|
|
2846
|
+
|
|
2847
|
+
editor[handler]($editable, $editor.data('options'));
|
|
2848
|
+
} else if (key.isEdit(event.keyCode)) {
|
|
2849
|
+
editor.recordUndo($editable);
|
|
2850
|
+
}
|
|
2851
|
+
});
|
|
2852
|
+
};
|
|
2853
|
+
|
|
2854
|
+
/**
|
|
2855
|
+
* attach eventhandler
|
|
2856
|
+
*
|
|
2857
|
+
* @param {Object} oLayoutInfo - layout Informations
|
|
2858
|
+
* @param {Object} options - user options include custom event handlers
|
|
2859
|
+
* @param {Function} options.enter - enter key handler
|
|
2860
|
+
*/
|
|
2861
|
+
this.attach = function (oLayoutInfo, options) {
|
|
2862
|
+
// handlers for editable
|
|
2863
|
+
this.bindKeyMap(oLayoutInfo, options.keyMap[agent.bMac ? 'mac' : 'pc']);
|
|
2864
|
+
oLayoutInfo.editable.on('mousedown', hMousedown);
|
|
2865
|
+
oLayoutInfo.editable.on('keyup mouseup', hToolbarAndPopoverUpdate);
|
|
2866
|
+
oLayoutInfo.editable.on('scroll', hScroll);
|
|
2867
|
+
oLayoutInfo.editable.on('paste', hPasteClipboardImage);
|
|
2868
|
+
|
|
2869
|
+
// handler for handle and popover
|
|
2870
|
+
oLayoutInfo.handle.on('mousedown', hHandleMousedown);
|
|
2871
|
+
oLayoutInfo.popover.on('click', hToolbarAndPopoverClick);
|
|
2872
|
+
oLayoutInfo.popover.on('mousedown', hToolbarAndPopoverMousedown);
|
|
2873
|
+
|
|
2874
|
+
// handlers for frame mode (toolbar, statusbar)
|
|
2875
|
+
if (!options.airMode) {
|
|
2876
|
+
// handler for drag and drop
|
|
2877
|
+
if (!options.disableDragAndDrop) {
|
|
2878
|
+
attachDragAndDropEvent(oLayoutInfo);
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
// handler for toolbar
|
|
2882
|
+
oLayoutInfo.toolbar.on('click', hToolbarAndPopoverClick);
|
|
2883
|
+
oLayoutInfo.toolbar.on('mousedown', hToolbarAndPopoverMousedown);
|
|
2884
|
+
|
|
2885
|
+
// handler for statusbar
|
|
2886
|
+
oLayoutInfo.statusbar.on('mousedown', hStatusbarMousedown);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
// handler for table dimension
|
|
2890
|
+
var $catcherContainer = options.airMode ? oLayoutInfo.popover :
|
|
2891
|
+
oLayoutInfo.toolbar;
|
|
2892
|
+
var $catcher = $catcherContainer.find('.note-dimension-picker-mousecatcher');
|
|
2893
|
+
$catcher.on('mousemove', hDimensionPickerMove);
|
|
2894
|
+
|
|
2895
|
+
// save selection when focusout
|
|
2896
|
+
oLayoutInfo.editable.on('blur', function () {
|
|
2897
|
+
editor.saveRange(oLayoutInfo.editable);
|
|
2898
|
+
});
|
|
2899
|
+
|
|
2900
|
+
// save options on editor
|
|
2901
|
+
oLayoutInfo.editor.data('options', options);
|
|
2902
|
+
|
|
2903
|
+
// ret styleWithCSS for backColor / foreColor clearing with 'inherit'.
|
|
2904
|
+
if (options.styleWithSpan && !agent.bMSIE) {
|
|
2905
|
+
// protect FF Error: NS_ERROR_FAILURE: Failure
|
|
2906
|
+
setTimeout(function () {
|
|
2907
|
+
document.execCommand('styleWithCSS', 0, true);
|
|
2908
|
+
}, 0);
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
// History
|
|
2912
|
+
oLayoutInfo.editable.data('NoteHistory', new History());
|
|
2913
|
+
|
|
2914
|
+
// basic event callbacks (lowercase)
|
|
2915
|
+
// enter, focus, blur, keyup, keydown
|
|
2916
|
+
if (options.onenter) {
|
|
2917
|
+
oLayoutInfo.editable.keypress(function (event) {
|
|
2918
|
+
if (event.keyCode === key.ENTER) { options.onenter(event); }
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
if (options.onfocus) { oLayoutInfo.editable.focus(options.onfocus); }
|
|
2923
|
+
if (options.onblur) { oLayoutInfo.editable.blur(options.onblur); }
|
|
2924
|
+
if (options.onkeyup) { oLayoutInfo.editable.keyup(options.onkeyup); }
|
|
2925
|
+
if (options.onkeydown) { oLayoutInfo.editable.keydown(options.onkeydown); }
|
|
2926
|
+
if (options.onpaste) { oLayoutInfo.editable.on('paste', options.onpaste); }
|
|
2927
|
+
|
|
2928
|
+
// callbacks for advanced features (camel)
|
|
2929
|
+
if (options.onToolbarClick) { oLayoutInfo.toolbar.click(options.onToolbarClick); }
|
|
2930
|
+
if (options.onChange) {
|
|
2931
|
+
var hChange = function () {
|
|
2932
|
+
options.onChange(oLayoutInfo.editable, oLayoutInfo.editable.html());
|
|
2933
|
+
};
|
|
2934
|
+
|
|
2935
|
+
if (agent.bMSIE) {
|
|
2936
|
+
var sDomEvents = 'DOMCharacterDataModified, DOMSubtreeModified, DOMNodeInserted';
|
|
2937
|
+
oLayoutInfo.editable.on(sDomEvents, hChange);
|
|
2938
|
+
} else {
|
|
2939
|
+
oLayoutInfo.editable.on('input', hChange);
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
// All editor status will be saved on editable with jquery's data
|
|
2944
|
+
// for support multiple editor with singleton object.
|
|
2945
|
+
oLayoutInfo.editable.data('callbacks', {
|
|
2946
|
+
onAutoSave: options.onAutoSave,
|
|
2947
|
+
onImageUpload: options.onImageUpload,
|
|
2948
|
+
onImageUploadError: options.onImageUploadError,
|
|
2949
|
+
onFileUpload: options.onFileUpload,
|
|
2950
|
+
onFileUploadError: options.onFileUpload
|
|
2951
|
+
});
|
|
2952
|
+
};
|
|
2953
|
+
|
|
2954
|
+
this.dettach = function (oLayoutInfo) {
|
|
2955
|
+
oLayoutInfo.editable.off();
|
|
2956
|
+
|
|
2957
|
+
oLayoutInfo.popover.off();
|
|
2958
|
+
oLayoutInfo.handle.off();
|
|
2959
|
+
oLayoutInfo.dialog.off();
|
|
2960
|
+
|
|
2961
|
+
if (oLayoutInfo.editor.data('options').airMode) {
|
|
2962
|
+
oLayoutInfo.dropzone.off();
|
|
2963
|
+
oLayoutInfo.toolbar.off();
|
|
2964
|
+
oLayoutInfo.statusbar.off();
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2967
|
+
};
|
|
2968
|
+
|
|
2969
|
+
/**
|
|
2970
|
+
* renderer
|
|
2971
|
+
*
|
|
2972
|
+
* rendering toolbar and editable
|
|
2973
|
+
*/
|
|
2974
|
+
var Renderer = function () {
|
|
2975
|
+
|
|
2976
|
+
/**
|
|
2977
|
+
* bootstrap button template
|
|
2978
|
+
*
|
|
2979
|
+
* @param {String} sLabel
|
|
2980
|
+
* @param {Object} [options]
|
|
2981
|
+
* @param {String} [options.event]
|
|
2982
|
+
* @param {String} [options.value]
|
|
2983
|
+
* @param {String} [options.title]
|
|
2984
|
+
* @param {String} [options.dropdown]
|
|
2985
|
+
*/
|
|
2986
|
+
var tplButton = function (sLabel, options) {
|
|
2987
|
+
var event = options.event;
|
|
2988
|
+
var value = options.value;
|
|
2989
|
+
var title = options.title;
|
|
2990
|
+
var className = options.className;
|
|
2991
|
+
var dropdown = options.dropdown;
|
|
2992
|
+
|
|
2993
|
+
return '<button type="button"' +
|
|
2994
|
+
' class="btn btn-default btn-sm btn-small' +
|
|
2995
|
+
(className ? ' ' + className : '') +
|
|
2996
|
+
(dropdown ? ' dropdown-toggle' : '') +
|
|
2997
|
+
'"' +
|
|
2998
|
+
(dropdown ? ' data-toggle="dropdown"' : '') +
|
|
2999
|
+
(title ? ' title="' + title + '"' : '') +
|
|
3000
|
+
(event ? ' data-event="' + event + '"' : '') +
|
|
3001
|
+
(value ? ' data-value=\'' + value + '\'' : '') +
|
|
3002
|
+
' tabindex="-1">' +
|
|
3003
|
+
sLabel +
|
|
3004
|
+
(dropdown ? ' <span class="caret"></span>' : '') +
|
|
3005
|
+
'</button>' +
|
|
3006
|
+
(dropdown || '');
|
|
3007
|
+
};
|
|
3008
|
+
|
|
3009
|
+
/**
|
|
3010
|
+
* bootstrap icon button template
|
|
3011
|
+
*
|
|
3012
|
+
* @param {String} sIconClass
|
|
3013
|
+
* @param {Object} [options]
|
|
3014
|
+
* @param {String} [options.event]
|
|
3015
|
+
* @param {String} [options.value]
|
|
3016
|
+
* @param {String} [options.title]
|
|
3017
|
+
* @param {String} [options.dropdown]
|
|
3018
|
+
*/
|
|
3019
|
+
var tplIconButton = function (sIconClass, options) {
|
|
3020
|
+
var sLabel = '<i class="' + sIconClass + '"></i>';
|
|
3021
|
+
return tplButton(sLabel, options);
|
|
3022
|
+
};
|
|
3023
|
+
|
|
3024
|
+
/**
|
|
3025
|
+
* bootstrap popover template
|
|
3026
|
+
*
|
|
3027
|
+
* @param {String} className
|
|
3028
|
+
* @param {String} content
|
|
3029
|
+
*/
|
|
3030
|
+
var tplPopover = function (className, content) {
|
|
3031
|
+
return '<div class="' + className + ' popover bottom in" style="display: none;">' +
|
|
3032
|
+
'<div class="arrow"></div>' +
|
|
3033
|
+
'<div class="popover-content">' +
|
|
3034
|
+
content +
|
|
3035
|
+
'</div>' +
|
|
3036
|
+
'</div>';
|
|
3037
|
+
};
|
|
3038
|
+
|
|
3039
|
+
/**
|
|
3040
|
+
* bootstrap dialog template
|
|
3041
|
+
*
|
|
3042
|
+
* @param {String} className
|
|
3043
|
+
* @param {String} [title]
|
|
3044
|
+
* @param {String} body
|
|
3045
|
+
* @param {String} [footer]
|
|
3046
|
+
*/
|
|
3047
|
+
var tplDialog = function (className, title, body, footer) {
|
|
3048
|
+
return '<div class="' + className + ' modal" aria-hidden="false">' +
|
|
3049
|
+
'<div class="modal-dialog">' +
|
|
3050
|
+
'<div class="modal-content">' +
|
|
3051
|
+
(title ?
|
|
3052
|
+
'<div class="modal-header">' +
|
|
3053
|
+
'<button type="button" class="close" aria-hidden="true" tabindex="-1">×</button>' +
|
|
3054
|
+
'<h4>' + title + '</h4>' +
|
|
3055
|
+
'</div>' : ''
|
|
3056
|
+
) +
|
|
3057
|
+
'<form class="note-modal-form">' +
|
|
3058
|
+
'<div class="modal-body">' +
|
|
3059
|
+
'<div class="row-fluid">' + body + '</div>' +
|
|
3060
|
+
'</div>' +
|
|
3061
|
+
(footer ?
|
|
3062
|
+
'<div class="modal-footer">' + footer + '</div>' : ''
|
|
3063
|
+
) +
|
|
3064
|
+
'</form>' +
|
|
3065
|
+
'</div>' +
|
|
3066
|
+
'</div>' +
|
|
3067
|
+
'</div>';
|
|
3068
|
+
};
|
|
3069
|
+
|
|
3070
|
+
var tplButtonInfo = {
|
|
3071
|
+
picture: function (lang) {
|
|
3072
|
+
return tplIconButton('fa fa-picture-o icon-picture', {
|
|
3073
|
+
event: 'showImageDialog',
|
|
3074
|
+
title: lang.image.image
|
|
3075
|
+
});
|
|
3076
|
+
},
|
|
3077
|
+
link: function (lang) {
|
|
3078
|
+
return tplIconButton('fa fa-link icon-link', {
|
|
3079
|
+
event: 'showLinkDialog',
|
|
3080
|
+
title: lang.link.link
|
|
3081
|
+
});
|
|
3082
|
+
},
|
|
3083
|
+
video: function (lang) {
|
|
3084
|
+
return tplIconButton('fa fa-youtube-play icon-play', {
|
|
3085
|
+
event: 'showVideoDialog',
|
|
3086
|
+
title: lang.video.video
|
|
3087
|
+
});
|
|
3088
|
+
},
|
|
3089
|
+
table: function (lang) {
|
|
3090
|
+
var dropdown = '<ul class="dropdown-menu">' +
|
|
3091
|
+
'<div class="note-dimension-picker">' +
|
|
3092
|
+
'<div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>' +
|
|
3093
|
+
'<div class="note-dimension-picker-highlighted"></div>' +
|
|
3094
|
+
'<div class="note-dimension-picker-unhighlighted"></div>' +
|
|
3095
|
+
'</div>' +
|
|
3096
|
+
'<div class="note-dimension-display"> 1 x 1 </div>' +
|
|
3097
|
+
'</ul>';
|
|
3098
|
+
return tplIconButton('fa fa-table icon-table', {
|
|
3099
|
+
title: lang.table.table,
|
|
3100
|
+
dropdown: dropdown
|
|
3101
|
+
});
|
|
3102
|
+
},
|
|
3103
|
+
style: function (lang, options) {
|
|
3104
|
+
var items = options.styleTags.reduce(function (memo, v) {
|
|
3105
|
+
var label = lang.style[v === 'p' ? 'normal' : v];
|
|
3106
|
+
return memo + '<li><a data-event="formatBlock" data-value="' + v + '">' +
|
|
3107
|
+
(
|
|
3108
|
+
(v === 'p' || v === 'pre') ? label :
|
|
3109
|
+
'<' + v + '>' + label + '</' + v + '>'
|
|
3110
|
+
) +
|
|
3111
|
+
'</a></li>';
|
|
3112
|
+
}, '');
|
|
3113
|
+
|
|
3114
|
+
return tplIconButton('fa fa-magic icon-magic', {
|
|
3115
|
+
title: lang.style.style,
|
|
3116
|
+
dropdown: '<ul class="dropdown-menu">' + items + '</ul>'
|
|
3117
|
+
});
|
|
3118
|
+
},
|
|
3119
|
+
fontname: function (lang, options) {
|
|
3120
|
+
var items = options.fontNames.reduce(function (memo, v) {
|
|
3121
|
+
return memo + '<li><a data-event="fontName" data-value="' + v + '">' +
|
|
3122
|
+
'<i class="fa fa-check icon-ok"></i> ' + v +
|
|
3123
|
+
'</a></li>';
|
|
3124
|
+
}, '');
|
|
3125
|
+
var sLabel = '<span class="note-current-fontname">' +
|
|
3126
|
+
options.defaultFontName +
|
|
3127
|
+
'</span>';
|
|
3128
|
+
return tplButton(sLabel, {
|
|
3129
|
+
title: lang.font.name,
|
|
3130
|
+
dropdown: '<ul class="dropdown-menu">' + items + '</ul>'
|
|
3131
|
+
});
|
|
3132
|
+
},
|
|
3133
|
+
fontsize: function (lang, options) {
|
|
3134
|
+
var items = options.fontSizes.reduce(function (memo, v) {
|
|
3135
|
+
return memo + '<li><a data-event="fontSize" data-value="' + v + '">' +
|
|
3136
|
+
'<i class="fa fa-check icon-ok"></i> ' + v +
|
|
3137
|
+
'</a></li>';
|
|
3138
|
+
}, '');
|
|
3139
|
+
|
|
3140
|
+
var sLabel = '<span class="note-current-fontsize">11</span>';
|
|
3141
|
+
return tplButton(sLabel, {
|
|
3142
|
+
title: lang.font.size,
|
|
3143
|
+
dropdown: '<ul class="dropdown-menu">' + items + '</ul>'
|
|
3144
|
+
});
|
|
3145
|
+
},
|
|
3146
|
+
|
|
3147
|
+
color: function (lang) {
|
|
3148
|
+
var colorButtonLabel = '<i class="fa fa-font icon-font" style="color:black;background-color:yellow;"></i>';
|
|
3149
|
+
var colorButton = tplButton(colorButtonLabel, {
|
|
3150
|
+
className: 'note-recent-color',
|
|
3151
|
+
title: lang.color.recent,
|
|
3152
|
+
event: 'color',
|
|
3153
|
+
value: '{"backColor":"yellow"}'
|
|
3154
|
+
});
|
|
3155
|
+
|
|
3156
|
+
var dropdown = '<ul class="dropdown-menu">' +
|
|
3157
|
+
'<li>' +
|
|
3158
|
+
'<div class="btn-group">' +
|
|
3159
|
+
'<div class="note-palette-title">' + lang.color.background + '</div>' +
|
|
3160
|
+
'<div class="note-color-reset" data-event="backColor"' +
|
|
3161
|
+
' data-value="inherit" title="' + lang.color.transparent + '">' +
|
|
3162
|
+
lang.color.setTransparent +
|
|
3163
|
+
'</div>' +
|
|
3164
|
+
'<div class="note-color-palette" data-target-event="backColor"></div>' +
|
|
3165
|
+
'</div>' +
|
|
3166
|
+
'<div class="btn-group">' +
|
|
3167
|
+
'<div class="note-palette-title">' + lang.color.foreground + '</div>' +
|
|
3168
|
+
'<div class="note-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">' +
|
|
3169
|
+
lang.color.resetToDefault +
|
|
3170
|
+
'</div>' +
|
|
3171
|
+
'<div class="note-color-palette" data-target-event="foreColor"></div>' +
|
|
3172
|
+
'</div>' +
|
|
3173
|
+
'</li>' +
|
|
3174
|
+
'</ul>';
|
|
3175
|
+
|
|
3176
|
+
var moreButton = tplButton('', {
|
|
3177
|
+
title: lang.color.more,
|
|
3178
|
+
dropdown: dropdown
|
|
3179
|
+
});
|
|
3180
|
+
|
|
3181
|
+
return colorButton + moreButton;
|
|
3182
|
+
},
|
|
3183
|
+
bold: function (lang) {
|
|
3184
|
+
return tplIconButton('fa fa-bold icon-bold', {
|
|
3185
|
+
event: 'bold',
|
|
3186
|
+
title: lang.font.bold
|
|
3187
|
+
});
|
|
3188
|
+
},
|
|
3189
|
+
italic: function (lang) {
|
|
3190
|
+
return tplIconButton('fa fa-italic icon-italic', {
|
|
3191
|
+
event: 'italic',
|
|
3192
|
+
title: lang.font.italic
|
|
3193
|
+
});
|
|
3194
|
+
},
|
|
3195
|
+
underline: function (lang) {
|
|
3196
|
+
return tplIconButton('fa fa-underline icon-underline', {
|
|
3197
|
+
event: 'underline',
|
|
3198
|
+
title: lang.font.underline
|
|
3199
|
+
});
|
|
3200
|
+
},
|
|
3201
|
+
strikethrough: function (lang) {
|
|
3202
|
+
return tplIconButton('fa fa-strikethrough icon-strikethrough', {
|
|
3203
|
+
event: 'strikethrough',
|
|
3204
|
+
title: lang.font.strikethrough
|
|
3205
|
+
});
|
|
3206
|
+
},
|
|
3207
|
+
superscript: function (lang) {
|
|
3208
|
+
return tplIconButton('fa fa-superscript icon-superscript', {
|
|
3209
|
+
event: 'superscript',
|
|
3210
|
+
title: lang.font.superscript
|
|
3211
|
+
});
|
|
3212
|
+
},
|
|
3213
|
+
subscript: function (lang) {
|
|
3214
|
+
return tplIconButton('fa fa-subscript icon-subscript', {
|
|
3215
|
+
event: 'subscript',
|
|
3216
|
+
title: lang.font.subscript
|
|
3217
|
+
});
|
|
3218
|
+
},
|
|
3219
|
+
clear: function (lang) {
|
|
3220
|
+
return tplIconButton('fa fa-eraser icon-eraser', {
|
|
3221
|
+
event: 'removeFormat',
|
|
3222
|
+
title: lang.font.clear
|
|
3223
|
+
});
|
|
3224
|
+
},
|
|
3225
|
+
ul: function (lang) {
|
|
3226
|
+
return tplIconButton('fa fa-list-ul icon-list-ul', {
|
|
3227
|
+
event: 'insertUnorderedList',
|
|
3228
|
+
title: lang.lists.unordered
|
|
3229
|
+
});
|
|
3230
|
+
},
|
|
3231
|
+
ol: function (lang) {
|
|
3232
|
+
return tplIconButton('fa fa-list-ol icon-list-ol', {
|
|
3233
|
+
event: 'insertOrderedList',
|
|
3234
|
+
title: lang.lists.ordered
|
|
3235
|
+
});
|
|
3236
|
+
},
|
|
3237
|
+
paragraph: function (lang) {
|
|
3238
|
+
var leftButton = tplIconButton('fa fa-align-left icon-align-left', {
|
|
3239
|
+
title: lang.paragraph.left,
|
|
3240
|
+
event: 'justifyLeft'
|
|
3241
|
+
});
|
|
3242
|
+
var centerButton = tplIconButton('fa fa-align-center icon-align-center', {
|
|
3243
|
+
title: lang.paragraph.center,
|
|
3244
|
+
event: 'justifyCenter'
|
|
3245
|
+
});
|
|
3246
|
+
var rightButton = tplIconButton('fa fa-align-right icon-align-right', {
|
|
3247
|
+
title: lang.paragraph.right,
|
|
3248
|
+
event: 'justifyRight'
|
|
3249
|
+
});
|
|
3250
|
+
var justifyButton = tplIconButton('fa fa-align-justify icon-align-justify', {
|
|
3251
|
+
title: lang.paragraph.justify,
|
|
3252
|
+
event: 'justifyFull'
|
|
3253
|
+
});
|
|
3254
|
+
|
|
3255
|
+
var outdentButton = tplIconButton('fa fa-outdent icon-indent-left', {
|
|
3256
|
+
title: lang.paragraph.outdent,
|
|
3257
|
+
event: 'outdent'
|
|
3258
|
+
});
|
|
3259
|
+
var indentButton = tplIconButton('fa fa-indent icon-indent-right', {
|
|
3260
|
+
title: lang.paragraph.indent,
|
|
3261
|
+
event: 'indent'
|
|
3262
|
+
});
|
|
3263
|
+
|
|
3264
|
+
var dropdown = '<div class="dropdown-menu">' +
|
|
3265
|
+
'<div class="note-align btn-group">' +
|
|
3266
|
+
leftButton + centerButton + rightButton + justifyButton +
|
|
3267
|
+
'</div>' +
|
|
3268
|
+
'<div class="note-list btn-group">' +
|
|
3269
|
+
indentButton + outdentButton +
|
|
3270
|
+
'</div>' +
|
|
3271
|
+
'</div>';
|
|
3272
|
+
|
|
3273
|
+
return tplIconButton('fa fa-align-left icon-align-left', {
|
|
3274
|
+
title: lang.paragraph.paragraph,
|
|
3275
|
+
dropdown: dropdown
|
|
3276
|
+
});
|
|
3277
|
+
},
|
|
3278
|
+
height: function (lang, options) {
|
|
3279
|
+
var items = options.lineHeights.reduce(function (memo, v) {
|
|
3280
|
+
return memo + '<li><a data-event="lineHeight" data-value="' + parseFloat(v) + '">' +
|
|
3281
|
+
'<i class="fa fa-check icon-ok"></i> ' + v +
|
|
3282
|
+
'</a></li>';
|
|
3283
|
+
}, '');
|
|
3284
|
+
|
|
3285
|
+
return tplIconButton('fa fa-text-height icon-text-height', {
|
|
3286
|
+
title: lang.font.height,
|
|
3287
|
+
dropdown: '<ul class="dropdown-menu">' + items + '</ul>'
|
|
3288
|
+
});
|
|
3289
|
+
|
|
3290
|
+
},
|
|
3291
|
+
help: function (lang) {
|
|
3292
|
+
return tplIconButton('fa fa-question icon-question', {
|
|
3293
|
+
event: 'showHelpDialog',
|
|
3294
|
+
title: lang.options.help
|
|
3295
|
+
});
|
|
3296
|
+
},
|
|
3297
|
+
fullscreen: function (lang) {
|
|
3298
|
+
return tplIconButton('fa fa-arrows-alt icon-fullscreen', {
|
|
3299
|
+
event: 'fullscreen',
|
|
3300
|
+
title: lang.options.fullscreen
|
|
3301
|
+
});
|
|
3302
|
+
},
|
|
3303
|
+
codeview: function (lang) {
|
|
3304
|
+
return tplIconButton('fa fa-code icon-code', {
|
|
3305
|
+
event: 'codeview',
|
|
3306
|
+
title: lang.options.codeview
|
|
3307
|
+
});
|
|
3308
|
+
},
|
|
3309
|
+
undo: function (lang) {
|
|
3310
|
+
return tplIconButton('fa fa-undo icon-undo', {
|
|
3311
|
+
event: 'undo',
|
|
3312
|
+
title: lang.history.undo
|
|
3313
|
+
});
|
|
3314
|
+
},
|
|
3315
|
+
redo: function (lang) {
|
|
3316
|
+
return tplIconButton('fa fa-repeat icon-repeat', {
|
|
3317
|
+
event: 'redo',
|
|
3318
|
+
title: lang.history.redo
|
|
3319
|
+
});
|
|
3320
|
+
}
|
|
3321
|
+
};
|
|
3322
|
+
|
|
3323
|
+
var tplPopovers = function (lang, options) {
|
|
3324
|
+
var tplLinkPopover = function () {
|
|
3325
|
+
var linkButton = tplIconButton('fa fa-edit icon-edit', {
|
|
3326
|
+
title: lang.link.edit,
|
|
3327
|
+
event: 'showLinkDialog'
|
|
3328
|
+
});
|
|
3329
|
+
var unlinkButton = tplIconButton('fa fa-unlink icon-unlink', {
|
|
3330
|
+
title: lang.link.unlink,
|
|
3331
|
+
event: 'unlink'
|
|
3332
|
+
});
|
|
3333
|
+
var content = '<a href="http://www.google.com" target="_blank">www.google.com</a> ' +
|
|
3334
|
+
'<div class="note-insert btn-group">' +
|
|
3335
|
+
linkButton + unlinkButton +
|
|
3336
|
+
'</div>';
|
|
3337
|
+
return tplPopover('note-link-popover', content);
|
|
3338
|
+
};
|
|
3339
|
+
|
|
3340
|
+
var tplImagePopover = function () {
|
|
3341
|
+
var fullButton = tplButton('<span class="note-fontsize-10">100%</span>', {
|
|
3342
|
+
title: lang.image.resizeFull,
|
|
3343
|
+
event: 'resize',
|
|
3344
|
+
value: '1'
|
|
3345
|
+
});
|
|
3346
|
+
var halfButton = tplButton('<span class="note-fontsize-10">50%</span>', {
|
|
3347
|
+
title: lang.image.resizeHalf,
|
|
3348
|
+
event: 'resize',
|
|
3349
|
+
value: '0.5'
|
|
3350
|
+
});
|
|
3351
|
+
var quarterButton = tplButton('<span class="note-fontsize-10">25%</span>', {
|
|
3352
|
+
title: lang.image.resizeQuarter,
|
|
3353
|
+
event: 'resize',
|
|
3354
|
+
value: '0.25'
|
|
3355
|
+
});
|
|
3356
|
+
|
|
3357
|
+
var leftButton = tplIconButton('fa fa-align-left icon-align-left', {
|
|
3358
|
+
title: lang.image.floatLeft,
|
|
3359
|
+
event: 'floatMe',
|
|
3360
|
+
value: 'left'
|
|
3361
|
+
});
|
|
3362
|
+
var rightButton = tplIconButton('fa fa-align-right icon-align-right', {
|
|
3363
|
+
title: lang.image.floatRight,
|
|
3364
|
+
event: 'floatMe',
|
|
3365
|
+
value: 'right'
|
|
3366
|
+
});
|
|
3367
|
+
var justifyButton = tplIconButton('fa fa-align-justify icon-align-justify', {
|
|
3368
|
+
title: lang.image.floatNone,
|
|
3369
|
+
event: 'floatMe',
|
|
3370
|
+
value: 'none'
|
|
3371
|
+
});
|
|
3372
|
+
|
|
3373
|
+
var removeButton = tplIconButton('fa fa-trash-o icon-trash', {
|
|
3374
|
+
title: lang.image.remove,
|
|
3375
|
+
event: 'removeMedia',
|
|
3376
|
+
value: 'none'
|
|
3377
|
+
});
|
|
3378
|
+
|
|
3379
|
+
var content = '<div class="btn-group">' + fullButton + halfButton + quarterButton + '</div>' +
|
|
3380
|
+
'<div class="btn-group">' + leftButton + rightButton + justifyButton + '</div>' +
|
|
3381
|
+
'<div class="btn-group">' + removeButton + '</div>';
|
|
3382
|
+
return tplPopover('note-image-popover', content);
|
|
3383
|
+
};
|
|
3384
|
+
|
|
3385
|
+
var tplAirPopover = function () {
|
|
3386
|
+
var content = '';
|
|
3387
|
+
for (var idx = 0, sz = options.airPopover.length; idx < sz; idx ++) {
|
|
3388
|
+
var group = options.airPopover[idx];
|
|
3389
|
+
content += '<div class="note-' + group[0] + ' btn-group">';
|
|
3390
|
+
for (var i = 0, szGroup = group[1].length; i < szGroup; i++) {
|
|
3391
|
+
content += tplButtonInfo[group[1][i]](lang, options);
|
|
3392
|
+
}
|
|
3393
|
+
content += '</div>';
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
return tplPopover('note-air-popover', content);
|
|
3397
|
+
};
|
|
3398
|
+
|
|
3399
|
+
return '<div class="note-popover">' +
|
|
3400
|
+
tplLinkPopover() +
|
|
3401
|
+
tplImagePopover() +
|
|
3402
|
+
(options.airMode ? tplAirPopover() : '') +
|
|
3403
|
+
'</div>';
|
|
3404
|
+
};
|
|
3405
|
+
|
|
3406
|
+
var tplHandles = function () {
|
|
3407
|
+
return '<div class="note-handle">' +
|
|
3408
|
+
'<div class="note-control-selection">' +
|
|
3409
|
+
'<div class="note-control-selection-bg"></div>' +
|
|
3410
|
+
'<div class="note-control-holder note-control-nw"></div>' +
|
|
3411
|
+
'<div class="note-control-holder note-control-ne"></div>' +
|
|
3412
|
+
'<div class="note-control-holder note-control-sw"></div>' +
|
|
3413
|
+
'<div class="note-control-sizing note-control-se"></div>' +
|
|
3414
|
+
'<div class="note-control-selection-info"></div>' +
|
|
3415
|
+
'</div>' +
|
|
3416
|
+
'</div>';
|
|
3417
|
+
};
|
|
3418
|
+
|
|
3419
|
+
/**
|
|
3420
|
+
* shortcut table template
|
|
3421
|
+
* @param {String} title
|
|
3422
|
+
* @param {String} body
|
|
3423
|
+
*/
|
|
3424
|
+
var tplShortcut = function (title, body) {
|
|
3425
|
+
return '<table class="note-shortcut">' +
|
|
3426
|
+
'<thead>' +
|
|
3427
|
+
'<tr><th></th><th>' + title + '</th></tr>' +
|
|
3428
|
+
'</thead>' +
|
|
3429
|
+
'<tbody>' + body + '</tbody>' +
|
|
3430
|
+
'</table>';
|
|
3431
|
+
};
|
|
3432
|
+
|
|
3433
|
+
var tplShortcutText = function (lang) {
|
|
3434
|
+
var body = '<tr><td>⌘ + B</td><td>' + lang.font.bold + '</td></tr>' +
|
|
3435
|
+
'<tr><td>⌘ + I</td><td>' + lang.font.italic + '</td></tr>' +
|
|
3436
|
+
'<tr><td>⌘ + U</td><td>' + lang.font.underline + '</td></tr>' +
|
|
3437
|
+
'<tr><td>⌘ + ⇧ + S</td><td>' + lang.font.strikethrough + '</td></tr>' +
|
|
3438
|
+
'<tr><td>⌘ + \\</td><td>' + lang.font.clear + '</td></tr>';
|
|
3439
|
+
|
|
3440
|
+
return tplShortcut(lang.shortcut.textFormatting, body);
|
|
3441
|
+
};
|
|
3442
|
+
|
|
3443
|
+
var tplShortcutAction = function (lang) {
|
|
3444
|
+
var body = '<tr><td>⌘ + Z</td><td>' + lang.history.undo + '</td></tr>' +
|
|
3445
|
+
'<tr><td>⌘ + ⇧ + Z</td><td>' + lang.history.redo + '</td></tr>' +
|
|
3446
|
+
'<tr><td>⌘ + ]</td><td>' + lang.paragraph.indent + '</td></tr>' +
|
|
3447
|
+
'<tr><td>⌘ + [</td><td>' + lang.paragraph.outdent + '</td></tr>' +
|
|
3448
|
+
'<tr><td>⌘ + ENTER</td><td>' + lang.hr.insert + '</td></tr>';
|
|
3449
|
+
|
|
3450
|
+
return tplShortcut(lang.shortcut.action, body);
|
|
3451
|
+
};
|
|
3452
|
+
|
|
3453
|
+
var tplShortcutPara = function (lang) {
|
|
3454
|
+
var body = '<tr><td>⌘ + ⇧ + L</td><td>' + lang.paragraph.left + '</td></tr>' +
|
|
3455
|
+
'<tr><td>⌘ + ⇧ + E</td><td>' + lang.paragraph.center + '</td></tr>' +
|
|
3456
|
+
'<tr><td>⌘ + ⇧ + R</td><td>' + lang.paragraph.right + '</td></tr>' +
|
|
3457
|
+
'<tr><td>⌘ + ⇧ + J</td><td>' + lang.paragraph.justify + '</td></tr>' +
|
|
3458
|
+
'<tr><td>⌘ + ⇧ + NUM7</td><td>' + lang.lists.ordered + '</td></tr>' +
|
|
3459
|
+
'<tr><td>⌘ + ⇧ + NUM8</td><td>' + lang.lists.unordered + '</td></tr>';
|
|
3460
|
+
|
|
3461
|
+
return tplShortcut(lang.shortcut.paragraphFormatting, body);
|
|
3462
|
+
};
|
|
3463
|
+
|
|
3464
|
+
var tplShortcutStyle = function (lang) {
|
|
3465
|
+
var body = '<tr><td>⌘ + NUM0</td><td>' + lang.style.normal + '</td></tr>' +
|
|
3466
|
+
'<tr><td>⌘ + NUM1</td><td>' + lang.style.h1 + '</td></tr>' +
|
|
3467
|
+
'<tr><td>⌘ + NUM2</td><td>' + lang.style.h2 + '</td></tr>' +
|
|
3468
|
+
'<tr><td>⌘ + NUM3</td><td>' + lang.style.h3 + '</td></tr>' +
|
|
3469
|
+
'<tr><td>⌘ + NUM4</td><td>' + lang.style.h4 + '</td></tr>' +
|
|
3470
|
+
'<tr><td>⌘ + NUM5</td><td>' + lang.style.h5 + '</td></tr>' +
|
|
3471
|
+
'<tr><td>⌘ + NUM6</td><td>' + lang.style.h6 + '</td></tr>';
|
|
3472
|
+
|
|
3473
|
+
return tplShortcut(lang.shortcut.documentStyle, body);
|
|
3474
|
+
};
|
|
3475
|
+
|
|
3476
|
+
var tplExtraShortcuts = function (lang, options) {
|
|
3477
|
+
var extraKeys = options.extraKeys;
|
|
3478
|
+
var body = '';
|
|
3479
|
+
for (var key in extraKeys) {
|
|
3480
|
+
if (extraKeys.hasOwnProperty(key)) {
|
|
3481
|
+
body += '<tr><td>' + key + '</td><td>' + extraKeys[key] + '</td></tr>';
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3485
|
+
return tplShortcut(lang.shortcut.extraKeys, body);
|
|
3486
|
+
};
|
|
3487
|
+
|
|
3488
|
+
var tplShortcutTable = function (lang, options) {
|
|
3489
|
+
var template = '<table class="note-shortcut-layout">' +
|
|
3490
|
+
'<tbody>' +
|
|
3491
|
+
'<tr><td>' + tplShortcutAction(lang, options) + '</td><td>' + tplShortcutText(lang, options) + '</td></tr>' +
|
|
3492
|
+
'<tr><td>' + tplShortcutStyle(lang, options) + '</td><td>' + tplShortcutPara(lang, options) + '</td></tr>';
|
|
3493
|
+
if (options.extraKeys) {
|
|
3494
|
+
template += '<tr><td colspan="2">' + tplExtraShortcuts(lang, options) + '</td></tr>';
|
|
3495
|
+
}
|
|
3496
|
+
template += '</tbody</table>';
|
|
3497
|
+
return template;
|
|
3498
|
+
};
|
|
3499
|
+
|
|
3500
|
+
var replaceMacKeys = function (sHtml) {
|
|
3501
|
+
return sHtml.replace(/⌘/g, 'Ctrl').replace(/⇧/g, 'Shift');
|
|
3502
|
+
};
|
|
3503
|
+
|
|
3504
|
+
var tplDialogs = function (lang, options) {
|
|
3505
|
+
var tplImageDialog = function () {
|
|
3506
|
+
var body = '<h5>' + lang.image.selectFromFiles + '</h5>' +
|
|
3507
|
+
'<input class="note-image-input" type="file" name="files" accept="image/*" />' +
|
|
3508
|
+
'<h5>' + lang.image.url + '</h5>' +
|
|
3509
|
+
'<input class="note-image-url form-control span12" type="text" />';
|
|
3510
|
+
var footer = '<button href="#" class="btn btn-primary note-image-btn disabled" disabled>' + lang.image.insert + '</button>';
|
|
3511
|
+
return tplDialog('note-image-dialog', lang.image.insert, body, footer);
|
|
3512
|
+
};
|
|
3513
|
+
|
|
3514
|
+
var tplLinkDialog = function () {
|
|
3515
|
+
var body = '<div class="form-group">' +
|
|
3516
|
+
'<label>' + lang.link.textToDisplay + '</label>' +
|
|
3517
|
+
'<input class="note-link-text form-control span12" type="text" />' +
|
|
3518
|
+
'</div>' +
|
|
3519
|
+
'<div class="form-group">' +
|
|
3520
|
+
'<label>' + lang.link.url + '</label>' +
|
|
3521
|
+
'<input class="note-link-url form-control span12" type="text" />' +
|
|
3522
|
+
'</div>' +
|
|
3523
|
+
(!options.disableLinkTarget ?
|
|
3524
|
+
'<div class="checkbox">' +
|
|
3525
|
+
'<label>' + '<input type="checkbox" checked> ' +
|
|
3526
|
+
lang.link.openInNewWindow +
|
|
3527
|
+
'</label>' +
|
|
3528
|
+
'</div>' : ''
|
|
3529
|
+
);
|
|
3530
|
+
var footer = '<button href="#" class="btn btn-primary note-link-btn disabled" disabled>' + lang.link.insert + '</button>';
|
|
3531
|
+
return tplDialog('note-link-dialog', lang.link.insert, body, footer);
|
|
3532
|
+
};
|
|
3533
|
+
|
|
3534
|
+
var tplVideoDialog = function () {
|
|
3535
|
+
var body = '<div class="form-group">' +
|
|
3536
|
+
'<label>' + lang.video.url + '</label> <small class="text-muted">' + lang.video.providers + '</small>' +
|
|
3537
|
+
'<input class="note-video-url form-control span12" type="text" />' +
|
|
3538
|
+
'</div>';
|
|
3539
|
+
var footer = '<button href="#" class="btn btn-primary note-video-btn disabled" disabled>' + lang.video.insert + '</button>';
|
|
3540
|
+
return tplDialog('note-video-dialog', lang.video.insert, body, footer);
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3543
|
+
var tplHelpDialog = function () {
|
|
3544
|
+
var body = '<a class="modal-close pull-right" aria-hidden="true" tabindex="-1">' + lang.shortcut.close + '</a>' +
|
|
3545
|
+
'<div class="title">' + lang.shortcut.shortcuts + '</div>' +
|
|
3546
|
+
(agent.bMac ? tplShortcutTable(lang, options) : replaceMacKeys(tplShortcutTable(lang, options))) +
|
|
3547
|
+
'<p class="text-center">' +
|
|
3548
|
+
'<a href="//hackerwins.github.io/summernote/" target="_blank">Summernote 0.5.2</a> · ' +
|
|
3549
|
+
'<a href="//github.com/HackerWins/summernote" target="_blank">Project</a> · ' +
|
|
3550
|
+
'<a href="//github.com/HackerWins/summernote/issues" target="_blank">Issues</a>' +
|
|
3551
|
+
'</p>';
|
|
3552
|
+
return tplDialog('note-help-dialog', '', body, '');
|
|
3553
|
+
};
|
|
3554
|
+
|
|
3555
|
+
return '<div class="note-dialog">' +
|
|
3556
|
+
tplImageDialog() +
|
|
3557
|
+
tplLinkDialog() +
|
|
3558
|
+
tplVideoDialog() +
|
|
3559
|
+
tplHelpDialog() +
|
|
3560
|
+
'</div>';
|
|
3561
|
+
};
|
|
3562
|
+
|
|
3563
|
+
var tplStatusbar = function () {
|
|
3564
|
+
return '<div class="note-resizebar">' +
|
|
3565
|
+
'<div class="note-icon-bar"></div>' +
|
|
3566
|
+
'<div class="note-icon-bar"></div>' +
|
|
3567
|
+
'<div class="note-icon-bar"></div>' +
|
|
3568
|
+
'</div>';
|
|
3569
|
+
};
|
|
3570
|
+
|
|
3571
|
+
var representShortcut = function (str) {
|
|
3572
|
+
if (agent.bMac) {
|
|
3573
|
+
str = str.replace('CMD', '⌘').replace('SHIFT', '⇧');
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
return str.replace('BACKSLASH', '\\')
|
|
3577
|
+
.replace('SLASH', '/')
|
|
3578
|
+
.replace('LEFTBRACKET', '[')
|
|
3579
|
+
.replace('RIGHTBRACKET', ']');
|
|
3580
|
+
};
|
|
3581
|
+
|
|
3582
|
+
/**
|
|
3583
|
+
* createTooltip
|
|
3584
|
+
*
|
|
3585
|
+
* @param {jQuery} $container
|
|
3586
|
+
* @param {Object} keyMap
|
|
3587
|
+
* @param {String} [sPlacement]
|
|
3588
|
+
*/
|
|
3589
|
+
var createTooltip = function ($container, keyMap, sPlacement) {
|
|
3590
|
+
var invertedKeyMap = func.invertObject(keyMap);
|
|
3591
|
+
var $buttons = $container.find('button');
|
|
3592
|
+
|
|
3593
|
+
$buttons.each(function (i, elBtn) {
|
|
3594
|
+
var $btn = $(elBtn);
|
|
3595
|
+
var sShortcut = invertedKeyMap[$btn.data('event')];
|
|
3596
|
+
if (sShortcut) {
|
|
3597
|
+
$btn.attr('title', function (i, v) {
|
|
3598
|
+
return v + ' (' + representShortcut(sShortcut) + ')';
|
|
3599
|
+
});
|
|
3600
|
+
}
|
|
3601
|
+
// bootstrap tooltip on btn-group bug
|
|
3602
|
+
// https://github.com/twitter/bootstrap/issues/5687
|
|
3603
|
+
}).tooltip({
|
|
3604
|
+
container: 'body',
|
|
3605
|
+
trigger: 'hover',
|
|
3606
|
+
placement: sPlacement || 'top'
|
|
3607
|
+
}).on('click', function () {
|
|
3608
|
+
$(this).tooltip('hide');
|
|
3609
|
+
});
|
|
3610
|
+
};
|
|
3611
|
+
|
|
3612
|
+
// createPalette
|
|
3613
|
+
var createPalette = function ($container, options) {
|
|
3614
|
+
var aaColor = options.colors;
|
|
3615
|
+
$container.find('.note-color-palette').each(function () {
|
|
3616
|
+
var $palette = $(this), sEvent = $palette.attr('data-target-event');
|
|
3617
|
+
var aPaletteContents = [];
|
|
3618
|
+
for (var row = 0, szRow = aaColor.length; row < szRow; row++) {
|
|
3619
|
+
var aColor = aaColor[row];
|
|
3620
|
+
var aButton = [];
|
|
3621
|
+
for (var col = 0, szCol = aColor.length; col < szCol; col++) {
|
|
3622
|
+
var sColor = aColor[col];
|
|
3623
|
+
aButton.push(['<button type="button" class="note-color-btn" style="background-color:', sColor,
|
|
3624
|
+
';" data-event="', sEvent,
|
|
3625
|
+
'" data-value="', sColor,
|
|
3626
|
+
'" title="', sColor,
|
|
3627
|
+
'" data-toggle="button" tabindex="-1"></button>'].join(''));
|
|
3628
|
+
}
|
|
3629
|
+
aPaletteContents.push('<div>' + aButton.join('') + '</div>');
|
|
3630
|
+
}
|
|
3631
|
+
$palette.html(aPaletteContents.join(''));
|
|
3632
|
+
});
|
|
3633
|
+
};
|
|
3634
|
+
|
|
3635
|
+
/**
|
|
3636
|
+
* create summernote layout (air mode)
|
|
3637
|
+
*
|
|
3638
|
+
* @param {jQuery} $holder
|
|
3639
|
+
* @param {Object} options
|
|
3640
|
+
*/
|
|
3641
|
+
this.createLayoutByAirMode = function ($holder, options) {
|
|
3642
|
+
var keyMap = options.keyMap[agent.bMac ? 'mac' : 'pc'];
|
|
3643
|
+
var langInfo = $.summernote.lang[options.lang];
|
|
3644
|
+
|
|
3645
|
+
var id = func.uniqueId();
|
|
3646
|
+
|
|
3647
|
+
$holder.addClass('note-air-editor note-editable');
|
|
3648
|
+
$holder.attr({
|
|
3649
|
+
'id': 'note-editor-' + id,
|
|
3650
|
+
'contentEditable': true
|
|
3651
|
+
});
|
|
3652
|
+
|
|
3653
|
+
var body = document.body;
|
|
3654
|
+
|
|
3655
|
+
// create Popover
|
|
3656
|
+
var $popover = $(tplPopovers(langInfo, options));
|
|
3657
|
+
$popover.addClass('note-air-layout');
|
|
3658
|
+
$popover.attr('id', 'note-popover-' + id);
|
|
3659
|
+
$popover.appendTo(body);
|
|
3660
|
+
createTooltip($popover, keyMap);
|
|
3661
|
+
createPalette($popover, options);
|
|
3662
|
+
|
|
3663
|
+
// create Handle
|
|
3664
|
+
var $handle = $(tplHandles());
|
|
3665
|
+
$handle.addClass('note-air-layout');
|
|
3666
|
+
$handle.attr('id', 'note-handle-' + id);
|
|
3667
|
+
$handle.appendTo(body);
|
|
3668
|
+
|
|
3669
|
+
// create Dialog
|
|
3670
|
+
var $dialog = $(tplDialogs(langInfo, options));
|
|
3671
|
+
$dialog.addClass('note-air-layout');
|
|
3672
|
+
$dialog.attr('id', 'note-dialog-' + id);
|
|
3673
|
+
$dialog.find('button.close, a.modal-close').click(function () {
|
|
3674
|
+
$(this).closest('.modal').modal('hide');
|
|
3675
|
+
});
|
|
3676
|
+
$dialog.appendTo(body);
|
|
3677
|
+
};
|
|
3678
|
+
|
|
3679
|
+
/**
|
|
3680
|
+
* create summernote layout (normal mode)
|
|
3681
|
+
*
|
|
3682
|
+
* @param {jQuery} $holder
|
|
3683
|
+
* @param {Object} options
|
|
3684
|
+
*/
|
|
3685
|
+
this.createLayoutByFrame = function ($holder, options) {
|
|
3686
|
+
//01. create Editor
|
|
3687
|
+
var $editor = $('<div class="note-editor"></div>');
|
|
3688
|
+
if (options.width) {
|
|
3689
|
+
$editor.width(options.width);
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
//02. statusbar (resizebar)
|
|
3693
|
+
if (options.height > 0) {
|
|
3694
|
+
$('<div class="note-statusbar">' + tplStatusbar() + '</div>').prependTo($editor);
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
//03. create Editable
|
|
3698
|
+
var isContentEditable = !$holder.is(':disabled');
|
|
3699
|
+
var $editable = $('<div class="note-editable" contentEditable="' + isContentEditable + '"></div>')
|
|
3700
|
+
.prependTo($editor);
|
|
3701
|
+
if (options.height) {
|
|
3702
|
+
$editable.height(options.height);
|
|
3703
|
+
}
|
|
3704
|
+
if (options.direction) {
|
|
3705
|
+
$editable.attr('dir', options.direction);
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
$editable.html(dom.html($holder) || dom.emptyPara);
|
|
3709
|
+
|
|
3710
|
+
//031. create codable
|
|
3711
|
+
$('<textarea class="note-codable"></textarea>').prependTo($editor);
|
|
3712
|
+
|
|
3713
|
+
var langInfo = $.summernote.lang[options.lang];
|
|
3714
|
+
|
|
3715
|
+
//04. create Toolbar
|
|
3716
|
+
var sToolbar = '';
|
|
3717
|
+
for (var idx = 0, sz = options.toolbar.length; idx < sz; idx ++) {
|
|
3718
|
+
var group = options.toolbar[idx];
|
|
3719
|
+
sToolbar += '<div class="note-' + group[0] + ' btn-group">';
|
|
3720
|
+
for (var i = 0, szGroup = group[1].length; i < szGroup; i++) {
|
|
3721
|
+
sToolbar += tplButtonInfo[group[1][i]](langInfo, options);
|
|
3722
|
+
}
|
|
3723
|
+
sToolbar += '</div>';
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
sToolbar = '<div class="note-toolbar btn-toolbar">' + sToolbar + '</div>';
|
|
3727
|
+
|
|
3728
|
+
var $toolbar = $(sToolbar).prependTo($editor);
|
|
3729
|
+
var keyMap = options.keyMap[agent.bMac ? 'mac' : 'pc'];
|
|
3730
|
+
createPalette($toolbar, options);
|
|
3731
|
+
createTooltip($toolbar, keyMap, 'bottom');
|
|
3732
|
+
|
|
3733
|
+
//05. create Popover
|
|
3734
|
+
var $popover = $(tplPopovers(langInfo, options)).prependTo($editor);
|
|
3735
|
+
createPalette($popover, options);
|
|
3736
|
+
createTooltip($popover, keyMap);
|
|
3737
|
+
|
|
3738
|
+
//06. handle(control selection, ...)
|
|
3739
|
+
$(tplHandles()).prependTo($editor);
|
|
3740
|
+
|
|
3741
|
+
//07. create Dialog
|
|
3742
|
+
var $dialog = $(tplDialogs(langInfo, options)).prependTo($editor);
|
|
3743
|
+
$dialog.find('button.close, a.modal-close').click(function () {
|
|
3744
|
+
$(this).closest('.modal').modal('hide');
|
|
3745
|
+
});
|
|
3746
|
+
|
|
3747
|
+
//08. create Dropzone
|
|
3748
|
+
$('<div class="note-dropzone"><div class="note-dropzone-message"></div></div>').prependTo($editor);
|
|
3749
|
+
|
|
3750
|
+
//09. Editor/Holder switch
|
|
3751
|
+
$editor.insertAfter($holder);
|
|
3752
|
+
$holder.hide();
|
|
3753
|
+
};
|
|
3754
|
+
|
|
3755
|
+
this.noteEditorFromHolder = function ($holder) {
|
|
3756
|
+
if ($holder.hasClass('note-air-editor')) {
|
|
3757
|
+
return $holder;
|
|
3758
|
+
} else if ($holder.next().hasClass('note-editor')) {
|
|
3759
|
+
return $holder.next();
|
|
3760
|
+
} else {
|
|
3761
|
+
return $();
|
|
3762
|
+
}
|
|
3763
|
+
};
|
|
3764
|
+
|
|
3765
|
+
/**
|
|
3766
|
+
* create summernote layout
|
|
3767
|
+
*
|
|
3768
|
+
* @param {jQuery} $holder
|
|
3769
|
+
* @param {Object} options
|
|
3770
|
+
*/
|
|
3771
|
+
this.createLayout = function ($holder, options) {
|
|
3772
|
+
if (this.noteEditorFromHolder($holder).length > 0) {
|
|
3773
|
+
return;
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
if (options.airMode) {
|
|
3777
|
+
this.createLayoutByAirMode($holder, options);
|
|
3778
|
+
} else {
|
|
3779
|
+
this.createLayoutByFrame($holder, options);
|
|
3780
|
+
}
|
|
3781
|
+
};
|
|
3782
|
+
|
|
3783
|
+
/**
|
|
3784
|
+
* returns layoutInfo from holder
|
|
3785
|
+
*
|
|
3786
|
+
* @param {jQuery} $holder - placeholder
|
|
3787
|
+
* @returns {Object}
|
|
3788
|
+
*/
|
|
3789
|
+
this.layoutInfoFromHolder = function ($holder) {
|
|
3790
|
+
var $editor = this.noteEditorFromHolder($holder);
|
|
3791
|
+
if (!$editor.length) { return; }
|
|
3792
|
+
|
|
3793
|
+
var layoutInfo = dom.buildLayoutInfo($editor);
|
|
3794
|
+
// cache all properties.
|
|
3795
|
+
for (var key in layoutInfo) {
|
|
3796
|
+
if (layoutInfo.hasOwnProperty(key)) {
|
|
3797
|
+
layoutInfo[key] = layoutInfo[key].call();
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
return layoutInfo;
|
|
3801
|
+
};
|
|
3802
|
+
|
|
3803
|
+
/**
|
|
3804
|
+
* removeLayout
|
|
3805
|
+
*
|
|
3806
|
+
* @param {jQuery} $holder - placeholder
|
|
3807
|
+
*/
|
|
3808
|
+
this.removeLayout = function ($holder) {
|
|
3809
|
+
var info = this.layoutInfoFromHolder($holder);
|
|
3810
|
+
if (!info) { return; }
|
|
3811
|
+
$holder.html(info.editable.html());
|
|
3812
|
+
|
|
3813
|
+
info.editor.remove();
|
|
3814
|
+
$holder.show();
|
|
3815
|
+
};
|
|
3816
|
+
};
|
|
3817
|
+
|
|
3818
|
+
// jQuery namespace for summernote
|
|
3819
|
+
$.summernote = $.summernote || {};
|
|
3820
|
+
|
|
3821
|
+
// extends default `settings`
|
|
3822
|
+
$.extend($.summernote, settings);
|
|
3823
|
+
|
|
3824
|
+
var renderer = new Renderer();
|
|
3825
|
+
var eventHandler = new EventHandler();
|
|
3826
|
+
|
|
3827
|
+
/**
|
|
3828
|
+
* extend jquery fn
|
|
3829
|
+
*/
|
|
3830
|
+
$.fn.extend({
|
|
3831
|
+
/**
|
|
3832
|
+
* initialize summernote
|
|
3833
|
+
* - create editor layout and attach Mouse and keyboard events.
|
|
3834
|
+
*
|
|
3835
|
+
* @param {Object} options
|
|
3836
|
+
* @returns {this}
|
|
3837
|
+
*/
|
|
3838
|
+
summernote: function (options) {
|
|
3839
|
+
// extend default options
|
|
3840
|
+
options = $.extend({}, $.summernote.options, options);
|
|
3841
|
+
|
|
3842
|
+
this.each(function (idx, elHolder) {
|
|
3843
|
+
var $holder = $(elHolder);
|
|
3844
|
+
|
|
3845
|
+
// createLayout with options
|
|
3846
|
+
renderer.createLayout($holder, options);
|
|
3847
|
+
|
|
3848
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
|
3849
|
+
eventHandler.attach(info, options);
|
|
3850
|
+
|
|
3851
|
+
// Textarea: auto filling the code before form submit.
|
|
3852
|
+
if (dom.isTextarea($holder[0])) {
|
|
3853
|
+
$holder.closest('form').submit(function () {
|
|
3854
|
+
$holder.html($holder.code());
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
});
|
|
3858
|
+
|
|
3859
|
+
// focus on first editable element
|
|
3860
|
+
if (this.first().length && options.focus) {
|
|
3861
|
+
var info = renderer.layoutInfoFromHolder(this.first());
|
|
3862
|
+
info.editable.focus();
|
|
3863
|
+
}
|
|
3864
|
+
|
|
3865
|
+
// callback on init
|
|
3866
|
+
if (this.length > 0 && options.oninit) {
|
|
3867
|
+
options.oninit();
|
|
3868
|
+
}
|
|
3869
|
+
|
|
3870
|
+
return this;
|
|
3871
|
+
},
|
|
3872
|
+
//
|
|
3873
|
+
|
|
3874
|
+
/**
|
|
3875
|
+
* get the HTML contents of note or set the HTML contents of note.
|
|
3876
|
+
*
|
|
3877
|
+
* @param {String} [sHTML] - HTML contents(optional, set)
|
|
3878
|
+
* @returns {this|String} - context(set) or HTML contents of note(get).
|
|
3879
|
+
*/
|
|
3880
|
+
code: function (sHTML) {
|
|
3881
|
+
// get the HTML contents of note
|
|
3882
|
+
if (sHTML === undefined) {
|
|
3883
|
+
var $holder = this.first();
|
|
3884
|
+
if ($holder.length === 0) { return; }
|
|
3885
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
|
3886
|
+
if (!!(info && info.editable)) {
|
|
3887
|
+
var bCodeview = info.editor.hasClass('codeview');
|
|
3888
|
+
if (bCodeview && agent.bCodeMirror) {
|
|
3889
|
+
info.codable.data('cmEditor').save();
|
|
3890
|
+
}
|
|
3891
|
+
return bCodeview ? info.codable.val() : info.editable.html();
|
|
3892
|
+
}
|
|
3893
|
+
return $holder.html();
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
// set the HTML contents of note
|
|
3897
|
+
this.each(function (i, elHolder) {
|
|
3898
|
+
var info = renderer.layoutInfoFromHolder($(elHolder));
|
|
3899
|
+
if (info && info.editable) { info.editable.html(sHTML); }
|
|
3900
|
+
});
|
|
3901
|
+
|
|
3902
|
+
return this;
|
|
3903
|
+
},
|
|
3904
|
+
|
|
3905
|
+
/**
|
|
3906
|
+
* destroy Editor Layout and dettach Key and Mouse Event
|
|
3907
|
+
* @returns {this}
|
|
3908
|
+
*/
|
|
3909
|
+
destroy: function () {
|
|
3910
|
+
this.each(function (idx, elHolder) {
|
|
3911
|
+
var $holder = $(elHolder);
|
|
3912
|
+
|
|
3913
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
|
3914
|
+
if (!info || !info.editable) { return; }
|
|
3915
|
+
eventHandler.dettach(info);
|
|
3916
|
+
renderer.removeLayout($holder);
|
|
3917
|
+
});
|
|
3918
|
+
|
|
3919
|
+
return this;
|
|
3920
|
+
}
|
|
3921
|
+
});
|
|
3922
|
+
}));
|