bootsy-rails3 2.0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +15 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +155 -0
  4. data/Rakefile +38 -0
  5. data/app/assets/images/bootsy/gallery-loader.gif +0 -0
  6. data/app/assets/images/bootsy/upload-loader.gif +0 -0
  7. data/app/assets/javascripts/bootsy.js +8 -0
  8. data/app/assets/javascripts/bootsy/bootstrap-wysihtml5.js +530 -0
  9. data/app/assets/javascripts/bootsy/bootstrap.file-input.js +122 -0
  10. data/app/assets/javascripts/bootsy/bootsy.js +305 -0
  11. data/app/assets/javascripts/bootsy/editor_options.js +80 -0
  12. data/app/assets/javascripts/bootsy/init.js +31 -0
  13. data/app/assets/javascripts/bootsy/locales/bootstrap-wysihtml5.pt-BR.js +50 -0
  14. data/app/assets/javascripts/bootsy/locales/bootsy.pt-BR.js +9 -0
  15. data/app/assets/javascripts/bootsy/translations.js +8 -0
  16. data/app/assets/javascripts/bootsy/wysihtml5.js +9564 -0
  17. data/app/assets/stylesheets/bootsy.css +3 -0
  18. data/app/assets/stylesheets/bootsy/bootstrap-submenu.css +47 -0
  19. data/app/assets/stylesheets/bootsy/bootstrap-wysihtml5.css +102 -0
  20. data/app/assets/stylesheets/bootsy/bootsy.css +161 -0
  21. data/app/controllers/bootsy/application_controller.rb +7 -0
  22. data/app/controllers/bootsy/images_controller.rb +80 -0
  23. data/app/helpers/bootsy/application_helper.rb +11 -0
  24. data/app/uploaders/bootsy/image_uploader.rb +35 -0
  25. data/app/views/bootsy/images/_image.html.erb +43 -0
  26. data/app/views/bootsy/images/_modal.html.erb +28 -0
  27. data/app/views/bootsy/images/_new.html.erb +8 -0
  28. data/config/cucumber.yml +8 -0
  29. data/config/locales/en.yml +27 -0
  30. data/config/locales/pt-BR.yml +27 -0
  31. data/config/routes.rb +11 -0
  32. data/db/migrate/20120624171333_create_bootsy_images.rb +9 -0
  33. data/db/migrate/20120628124845_create_bootsy_image_galleries.rb +8 -0
  34. data/lib/bootsy-rails3.rb +66 -0
  35. data/lib/bootsy/activerecord/image.rb +11 -0
  36. data/lib/bootsy/activerecord/image_gallery.rb +8 -0
  37. data/lib/bootsy/container.rb +31 -0
  38. data/lib/bootsy/core_ext.rb +2 -0
  39. data/lib/bootsy/engine.rb +31 -0
  40. data/lib/bootsy/form_builder.rb +7 -0
  41. data/lib/bootsy/form_helper.rb +68 -0
  42. data/lib/bootsy/simple_form/bootsy_input.rb +11 -0
  43. data/lib/bootsy/version.rb +3 -0
  44. data/lib/generators/bootsy/USAGE +12 -0
  45. data/lib/generators/bootsy/install_generator.rb +50 -0
  46. data/lib/generators/bootsy/templates/bootsy.rb +64 -0
  47. data/lib/tasks/bootsy_tasks.rake +4 -0
  48. data/lib/tasks/cucumber.rake +65 -0
  49. metadata +137 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzRkNDkzMDhmYWMzYTYxMjFiODQyYjNkMTM2ZjAyNWQxYzFmOWZjOQ==
5
+ data.tar.gz: !binary |-
6
+ ZTE2ZjY2ZjM2OTFjZjUxZDkyMjc1MDg2NjkzMjZiNDlkMGNiMDA5Yw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NzljOWE4NWJjZjY4ZmNkMjcxZjgwN2M5NjU5YWJmNjdhMDZkMzhlMjk4ZGRj
10
+ ZDBkMjZhY2JlMzkxN2M0OGUzZjFkZWMzYTQ2ZGNjZTRlZTI4OGFkYjFkZjIy
11
+ YjI1YjJiN2I0ZTYzYWE1M2RkNjc2NjAzNDIwMWJhMGI1MDM0ODQ=
12
+ data.tar.gz: !binary |-
13
+ OTkxZGNjOTVhMTkwZmI3OWE5NmRhODY4M2IxYzM5MDU1MjFhZmUzNGQ3Y2Q5
14
+ MDE4NjkwMTFhN2EzMGYzNWI2ZDAxNDBmYTRjZDMwMTAwMGYwM2IyZGY2ZTJl
15
+ YWQ5NmY3NjU5OTMyYzE5MmIzODQwZmM2Mzk0ZmIyZTVkMDFhYjY=
@@ -0,0 +1,20 @@
1
+ Copyright 2012-2014 Volmer Soares
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,155 @@
1
+ # Bootsy
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/bootsy.png)](http://badge.fury.io/rb/bootsy)
4
+ [![Build Status](https://secure.travis-ci.org/volmer/bootsy.png?branch=master)](http://travis-ci.org/volmer/bootsy)
5
+ [![Dependency Status](https://gemnasium.com/volmer/bootsy.png)](https://gemnasium.com/volmer/bootsy)
6
+ [![Code Climate](https://codeclimate.com/github/volmer/bootsy.png)](https://codeclimate.com/github/volmer/bootsy)
7
+ [![Coverage Status](https://coveralls.io/repos/volmer/bootsy/badge.png?branch=master)](https://coveralls.io/r/volmer/bootsy)
8
+
9
+ *Bootsy* is a WYSIWYG solution for Rails based on [Bootstrap-wysihtml5](https://github.com/jhollingworth/bootstrap-wysihtml5) which includes image uploads via [CarrierWave](https://github.com/carrierwaveuploader/carrierwave).
10
+
11
+ ### Live demo
12
+
13
+ * [bootsy-demo.herokuapp.com](http://bootsy-demo.herokuapp.com/)
14
+ [![image](https://f.cloud.github.com/assets/301187/1365250/e1b7ba80-3854-11e3-9bfe-8bd1e090aca8.png)](http://bootsy-demo.herokuapp.com/)
15
+
16
+
17
+ ## Requirements
18
+
19
+ * Ruby `2.0` or `1.9.3`;
20
+ * ImageMagick or GraphicsMagick (for MiniMagick);
21
+ * Rails `4.0`;
22
+ * [Bootstrap `3`](http://getbootstrap.com/) properly added to your application.
23
+
24
+
25
+ ## Installation
26
+
27
+ 1. Add Bootsy to your Gemfile:
28
+ ```ruby
29
+ gem 'bootsy'
30
+ ```
31
+
32
+ 2. Run the bundle command to install it:
33
+ ```console
34
+ bundle install
35
+ ```
36
+
37
+ 3. Run the install generator:
38
+ ```console
39
+ rails generate bootsy:install
40
+ ```
41
+ It will include the javascripts and stylesheets in the assets pipeline,
42
+ create the `bootsy.rb` initializer and add a copy of the english translations.
43
+
44
+ 4. Add and run migrations (if you're using ActiveRecord):
45
+ ```console
46
+ rake bootsy:install:migrations
47
+ rake db:migrate
48
+ ```
49
+
50
+
51
+ ## Usage
52
+
53
+ Just call the brand new method `bootsy_area` in your `FormBuilder` instances, the
54
+ same way you'd call the basic `textarea` method. Example:
55
+ ```erb
56
+ <%= form_for(@post) do |f| %>
57
+ <%= f.label :title %>
58
+ <%= f.text_field :title %>
59
+
60
+ <%= f.label :content %>
61
+ <%= f.bootsy_area :content %>
62
+
63
+ <%= f.submit %>
64
+ <% end %>
65
+ ```
66
+
67
+ Bootsy will group the uploaded images as galleries and associate them to one of
68
+ your models. For example, if you have a `Post` model and you want to use `bootsy_area`
69
+ with it, then you should include the `Bootsy::Container` module:
70
+ ```ruby
71
+ class Post < ActiveRecord::Base
72
+ include Bootsy::Container
73
+ end
74
+ ```
75
+
76
+ Don't forget to ensure the association of new instances of your model with Bootsy
77
+ image galleries. For `strong_parameters`, you must allow the parameter `bootsy_image_gallery_id`
78
+ in your controllers. Example:
79
+ ```ruby
80
+ private
81
+ # Never trust parameters from the scary internet, only allow the white list through.
82
+ def post_params
83
+ params.require(:post).permit(:title, :content, :bootsy_image_gallery_id)
84
+ end
85
+ ```
86
+
87
+
88
+ ## Bootsy with [Simple Form](https://github.com/plataformatec/simple_form) builders
89
+
90
+ Just use the brand new input type `bootsy` in your `SimpleForm::FormBuilder` instances,
91
+ in the same way you would use the basic `text` input. Example:
92
+ ```erb
93
+ <%= simple_form_for @post do |f| %>
94
+ <%= f.input :title %>
95
+
96
+ <%= f.input :content, as: :bootsy %>
97
+
98
+ <%= f.button :submit %>
99
+ <% end %>
100
+ ```
101
+
102
+
103
+ ## Editor options
104
+
105
+ It is possible to customize how the editor is displayed and its behavior by passing
106
+ a hash `editor_options` to your `bootsy_area`.
107
+
108
+
109
+ ### Buttons
110
+
111
+ You can enable/disable the buttons available on the editor. For example, if you
112
+ want to disable the link and color buttons:
113
+ ```erb
114
+ <%= f.bootsy_area :my_attribute, editor_options: {link: false, color: false} %>
115
+ ```
116
+ Available options are: `:font_styles`, `:emphasis`, `:lists`, `:html`, `:link`, `:image` and `:color`.
117
+
118
+
119
+ ### Alert for usaved changes
120
+
121
+ By default, Bootsy alerts for unsaved changes if the user attempts to unload
122
+ the window. You can disable this behaviour by doing:
123
+ ```erb
124
+ <%= f.bootsy_area :my_attribute, editor_options: {alert_unsaved: false} %>
125
+ ```
126
+
127
+ ## Uploader
128
+
129
+ It's also possible to use Bootsy without the image upload feature. Just call
130
+ `bootsy_area` in a form builder not associated to a `Bootsy::Container` model.
131
+ This way users can insert images in their texts by providing an image url.
132
+
133
+
134
+ ## Configuration
135
+
136
+ You can set the default editor options, image sizes available (small, medium,
137
+ large and/or its original), its dimensions and more. Take a look at the initalizer
138
+ file, `/config/initializers/bootsy.rb`.
139
+
140
+
141
+ ## I18n
142
+
143
+ Bootsy defines some i18n keys. The english translation is automatically added
144
+ to your `config/locales` directory as `bootsy.en.yml`. You can follow that template
145
+ in order to translate Bootsy to your language. You can find some examples
146
+ [here](https://github.com/volmer/bootsy/tree/master/config/locales). It is also
147
+ necessary to add a translation for Bootstrap-wysihtml5, the javascript editor, in
148
+ your assets pipeline. Instructions [here](https://github.com/jhollingworth/bootstrap-wysihtml5#i18n).
149
+ If you are using the alert for unsaved changes, you have to define a translation
150
+ for it as well. Just follow [this example](https://github.com/volmer/bootsy/tree/master/app/assets/bootsy/locales/bootsy.pt-BR.js).
151
+
152
+
153
+ ## License
154
+
155
+ MIT License. Copyright 2012-2014 Volmer Soares
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Bootsy'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ require 'cucumber/rake/task'
26
+ require 'rspec/core/rake_task'
27
+ require 'coveralls/rake/task'
28
+
29
+ Coveralls::RakeTask.new
30
+
31
+ task default: [:spec, :cucumber, 'coveralls:push']
32
+
33
+ RSpec::Core::RakeTask.new(:spec)
34
+
35
+ Cucumber::Rake::Task.new do |t|
36
+ # Uncomment this line when cucumber/multi_test work with minitest.
37
+ # t.cucumber_opts = %w{--format pretty -s}
38
+ end
@@ -0,0 +1,8 @@
1
+ //= require jquery.remotipart
2
+ //= require bootsy/wysihtml5
3
+ //= require bootsy/bootstrap-wysihtml5
4
+ //= require bootsy/bootsy
5
+ //= require bootsy/bootstrap.file-input.js
6
+ //= require bootsy/init
7
+ //= require bootsy/editor_options
8
+ //= require bootsy/translations
@@ -0,0 +1,530 @@
1
+ !function($, wysi) {
2
+ "use strict";
3
+
4
+ var tpl = {
5
+ "font-styles": function(locale, options) {
6
+ var size = (options && options.size) ? ' btn-'+options.size : '';
7
+ return "<li class='dropdown'>" +
8
+ "<a class='btn btn-default dropdown-toggle" + size + "' data-toggle='dropdown' href='#' title='" + locale.font_styles.title + "'>" +
9
+ "<i class='glyphicon glyphicon-font'></i>&nbsp;<span class='current-font'>" + locale.font_styles.normal + "</span>&nbsp;<b class='caret'></b>" +
10
+ "</a>" +
11
+ "<ul class='dropdown-menu'>" +
12
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='div' tabindex='-1' role='menuitem'>" + locale.font_styles.normal + "</a></li>" +
13
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h1' tabindex='-1' role='menuitem'>" + locale.font_styles.h1 + "</a></li>" +
14
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h2' tabindex='-1' role='menuitem'>" + locale.font_styles.h2 + "</a></li>" +
15
+ "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h3' tabindex='-1' role='menuitem'>" + locale.font_styles.h3 + "</a></li>" +
16
+ "</ul>" +
17
+ "</li>";
18
+ },
19
+
20
+ "emphasis": function(locale, options) {
21
+ var size = (options && options.size) ? ' btn-'+options.size : '';
22
+ return "<li>" +
23
+ "<div class='btn-group'>" +
24
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='bold' title='CTRL+B' tabindex='-1'>" + locale.emphasis.bold + "</a>" +
25
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='italic' title='CTRL+I' tabindex='-1'>" + locale.emphasis.italic + "</a>" +
26
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='underline' title='CTRL+U' tabindex='-1'>" + locale.emphasis.underline + "</a>" +
27
+ "</div>" +
28
+ "</li>";
29
+ },
30
+
31
+ "lists": function(locale, options) {
32
+ var size = (options && options.size) ? ' btn-'+options.size : '';
33
+ return "<li>" +
34
+ "<div class='btn-group'>" +
35
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='insertUnorderedList' title='" + locale.lists.unordered + "' tabindex='-1'><i class='glyphicon glyphicon-list'></i></a>" +
36
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='insertOrderedList' title='" + locale.lists.ordered + "' tabindex='-1'><i class='glyphicon glyphicon-th-list'></i></a>" +
37
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='Outdent' title='" + locale.lists.outdent + "' tabindex='-1'><i class='glyphicon glyphicon-indent-left'></i></a>" +
38
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='Indent' title='" + locale.lists.indent + "' tabindex='-1'><i class='glyphicon glyphicon-indent-right'></i></a>" +
39
+ "</div>" +
40
+ "</li>";
41
+ },
42
+
43
+ "link": function(locale, options) {
44
+ var size = (options && options.size) ? ' btn-'+options.size : '';
45
+ return "<li>" +
46
+ "<div class='bootstrap-wysihtml5-insert-link-modal modal fade' tabindex='-1' role='dialog' aria-hidden='true'>" +
47
+ "<div class='modal-dialog'>" +
48
+ "<div class='modal-content'>" +
49
+ "<div class='modal-header'>" +
50
+ "<button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
51
+ "<h3 class='modal-title'>" + locale.link.insert + "</h3>" +
52
+ "</div>" +
53
+ "<div class='modal-body'>" +
54
+ "<input value='http://' class='bootstrap-wysihtml5-insert-link-url form-control input-lg'>" +
55
+ "</div>" +
56
+ "<div class='modal-footer'>" +
57
+ "<a href='#' class='btn btn-default' data-dismiss='modal'>" + locale.link.cancel + "</a>" +
58
+ "<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.link.insert + "</a>" +
59
+ "</div>" +
60
+ "</div>" +
61
+ "</div>" +
62
+ "</div>" +
63
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='createLink' title='" + locale.link.insert + "' tabindex='-1'><i class='glyphicon glyphicon-share'></i></a>" +
64
+ "</li>";
65
+ },
66
+
67
+ "image": function(locale, options) {
68
+ var size = (options && options.size) ? ' btn-'+options.size : '';
69
+ return "<li>" +
70
+ "<div class='bootstrap-wysihtml5-insert-image-modal modal fade' tabindex='-1' role='dialog' aria-hidden='true'>" +
71
+ "<div class='modal-dialog'>" +
72
+ "<div class='modal-content'>" +
73
+ "<div class='modal-header'>" +
74
+ "<button type='button' class='close' data-dismiss='modal' aria-hidden='true'>&times;</button>" +
75
+ "<h3 class='modal-title'>" + locale.image.insert + "</h3>" +
76
+ "</div>" +
77
+ "<div class='modal-body'>" +
78
+ "<input value='http://' class='bootstrap-wysihtml5-insert-image-url form-control input-lg'>" +
79
+ "</div>" +
80
+ "<div class='modal-footer'>" +
81
+ "<a href='#' class='btn btn-default ' data-dismiss='modal'>" + locale.image.cancel + "</a>" +
82
+ "<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.image.insert + "</a>" +
83
+ "</div>" +
84
+ "</div>" +
85
+ "</div>" +
86
+ "</div>" +
87
+ "<a class='btn btn-default " + size + "' data-wysihtml5-command='insertImage' title='" + locale.image.insert + "' tabindex='-1'><i class='glyphicon glyphicon-picture'></i></a>" +
88
+ "</li>";
89
+ },
90
+
91
+ "html": function(locale, options) {
92
+ var size = (options && options.size) ? ' btn-'+options.size : '';
93
+ return "<li>" +
94
+ "<div class='btn-group'>" +
95
+ "<a class='btn btn-default " + size + "' data-wysihtml5-action='change_view' title='" + locale.html.edit + "' tabindex='-1'><i class='glyphicon glyphicon-pencil'></i></a>" +
96
+ "</div>" +
97
+ "</li>";
98
+ },
99
+
100
+ "color": function(locale, options) {
101
+ var size = (options && options.size) ? ' btn-'+options.size : '';
102
+ return "<li class='dropdown'>" +
103
+ "<a class='btn btn-default dropdown-toggle" + size + "' data-toggle='dropdown' href='#' tabindex='-1' title='" + locale.colours.title + "'>" +
104
+ "<span class='current-color'>" + locale.colours.black + "</span>&nbsp;<b class='caret'></b>" +
105
+ "</a>" +
106
+ "<ul class='dropdown-menu'>" +
107
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='black'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='black' role='menuitem'>" + locale.colours.black + "</a></li>" +
108
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='silver'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='silver' role='menuitem'>" + locale.colours.silver + "</a></li>" +
109
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='gray'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='gray' role='menuitem'>" + locale.colours.gray + "</a></li>" +
110
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='maroon'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='maroon' role='menuitem'>" + locale.colours.maroon + "</a></li>" +
111
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='red'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='red' role='menuitem'>" + locale.colours.red + "</a></li>" +
112
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='purple'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='purple' role='menuitem'>" + locale.colours.purple + "</a></li>" +
113
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='green'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='green' role='menuitem'>" + locale.colours.green + "</a></li>" +
114
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='olive'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='olive' role='menuitem'>" + locale.colours.olive + "</a></li>" +
115
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='navy'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='navy' role='menuitem'>" + locale.colours.navy + "</a></li>" +
116
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='blue'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='blue' role='menuitem'>" + locale.colours.blue + "</a></li>" +
117
+ "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='orange'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='orange' role='menuitem'>" + locale.colours.orange + "</a></li>" +
118
+ "</ul>" +
119
+ "</li>";
120
+ }
121
+ };
122
+
123
+ var templates = function(key, locale, options) {
124
+ return tpl[key](locale, options);
125
+ };
126
+
127
+
128
+ var Wysihtml5 = function(el, options) {
129
+ this.el = el;
130
+ var toolbarOpts = options || defaultOptions;
131
+ for(var t in toolbarOpts.customTemplates) {
132
+ tpl[t] = toolbarOpts.customTemplates[t];
133
+ }
134
+ this.toolbar = this.createToolbar(el, toolbarOpts);
135
+ this.editor = this.createEditor(options);
136
+
137
+ window.editor = this.editor;
138
+
139
+ $('iframe.wysihtml5-sandbox').each(function(i, el){
140
+ $(el.contentWindow).off('focus.wysihtml5').on({
141
+ 'focus.wysihtml5' : function(){
142
+ $('li.dropdown').removeClass('open');
143
+ }
144
+ });
145
+ });
146
+ };
147
+
148
+ Wysihtml5.prototype = {
149
+
150
+ constructor: Wysihtml5,
151
+
152
+ createEditor: function(options) {
153
+ options = options || {};
154
+
155
+ // Add the toolbar to a clone of the options object so multiple instances
156
+ // of the WYISYWG don't break because "toolbar" is already defined
157
+ options = $.extend(true, {}, options);
158
+ options.toolbar = this.toolbar[0];
159
+
160
+ var editor = new wysi.Editor(this.el[0], options);
161
+
162
+ if(options && options.events) {
163
+ for(var eventName in options.events) {
164
+ editor.on(eventName, options.events[eventName]);
165
+ }
166
+ }
167
+ return editor;
168
+ },
169
+
170
+ createToolbar: function(el, options) {
171
+ var self = this;
172
+ var toolbar = $("<ul/>", {
173
+ 'class' : "wysihtml5-toolbar",
174
+ 'style': "display:none"
175
+ });
176
+ var culture = options.locale || defaultOptions.locale || "en";
177
+ for(var key in defaultOptions) {
178
+ var value = false;
179
+
180
+ if(options[key] !== undefined) {
181
+ if(options[key] === true) {
182
+ value = true;
183
+ }
184
+ } else {
185
+ value = defaultOptions[key];
186
+ }
187
+
188
+ if(value === true) {
189
+ toolbar.append(templates(key, locale[culture], options));
190
+
191
+ if(key === "html") {
192
+ this.initHtml(toolbar);
193
+ }
194
+
195
+ if(key === "link") {
196
+ this.initInsertLink(toolbar);
197
+ }
198
+
199
+ if(key === "image") {
200
+ this.initInsertImage(toolbar);
201
+ }
202
+
203
+ if(key == "customCommand") {
204
+ this.initCustomCommand(toolbar, options.customCommandCallback);
205
+ }
206
+ }
207
+ }
208
+
209
+ if(options.toolbar) {
210
+ for(key in options.toolbar) {
211
+ toolbar.append(options.toolbar[key]);
212
+ }
213
+ }
214
+
215
+ toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
216
+ var target = e.target || e.srcElement;
217
+ var el = $(target);
218
+ self.toolbar.find('.current-font').text(el.html());
219
+ });
220
+
221
+ toolbar.find("a[data-wysihtml5-command='foreColor']").click(function(e) {
222
+ var target = e.target || e.srcElement;
223
+ var el = $(target);
224
+ self.toolbar.find('.current-color').text(el.html());
225
+ });
226
+
227
+ this.el.before(toolbar);
228
+
229
+ return toolbar;
230
+ },
231
+
232
+ initHtml: function(toolbar) {
233
+ var changeViewSelector = "a[data-wysihtml5-action='change_view']";
234
+ toolbar.find(changeViewSelector).click(function(e) {
235
+ toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
236
+ });
237
+ },
238
+
239
+ initInsertImage: function(toolbar) {
240
+ var self = this;
241
+ var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
242
+ var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
243
+ var insertButton = insertImageModal.find('a.btn-primary');
244
+ var initialValue = urlInput.val();
245
+ var caretBookmark;
246
+
247
+ var insertImage = function() {
248
+ var url = urlInput.val();
249
+ urlInput.val(initialValue);
250
+ self.editor.currentView.element.focus();
251
+ if (caretBookmark) {
252
+ self.editor.composer.selection.setBookmark(caretBookmark);
253
+ caretBookmark = null;
254
+ }
255
+ self.editor.composer.commands.exec("insertImage", url);
256
+ };
257
+
258
+ urlInput.keypress(function(e) {
259
+ if(e.which == 13) {
260
+ insertImage();
261
+ insertImageModal.modal('hide');
262
+ }
263
+ });
264
+
265
+ insertButton.click(insertImage);
266
+
267
+ insertImageModal.on('shown', function() {
268
+ urlInput.focus();
269
+ });
270
+
271
+ insertImageModal.on('hide', function() {
272
+ self.editor.currentView.element.focus();
273
+ });
274
+
275
+ toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
276
+ var activeButton = $(this).hasClass("wysihtml5-command-active");
277
+
278
+ if (!activeButton) {
279
+ self.editor.currentView.element.focus(false);
280
+ caretBookmark = self.editor.composer.selection.getBookmark();
281
+ insertImageModal.appendTo('body').modal('show');
282
+ insertImageModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
283
+ e.stopPropagation();
284
+ });
285
+ return false;
286
+ }
287
+ else {
288
+ return true;
289
+ }
290
+ });
291
+ },
292
+
293
+ initCustomCommand: function(toolbar, callback) {
294
+ var self = this;
295
+
296
+ toolbar.find('a[data-wysihtml5-command=customCommand]').click(function() {
297
+ var activeButton = $(this).hasClass("wysihtml5-command-active");
298
+
299
+ if (!activeButton) {
300
+ callback(self.editor);
301
+ return false;
302
+ }
303
+ else {
304
+ return true;
305
+ }
306
+ });
307
+ },
308
+
309
+ initInsertLink: function(toolbar) {
310
+ var self = this;
311
+ var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
312
+ var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
313
+ var insertButton = insertLinkModal.find('a.btn-primary');
314
+ var initialValue = urlInput.val();
315
+ var caretBookmark;
316
+
317
+ var insertLink = function() {
318
+ var url = urlInput.val();
319
+ urlInput.val(initialValue);
320
+ self.editor.currentView.element.focus();
321
+ if (caretBookmark) {
322
+ self.editor.composer.selection.setBookmark(caretBookmark);
323
+ caretBookmark = null;
324
+ }
325
+ self.editor.composer.commands.exec("createLink", {
326
+ href: url,
327
+ target: "_blank",
328
+ rel: "nofollow"
329
+ });
330
+ };
331
+ var pressedEnter = false;
332
+
333
+ urlInput.keypress(function(e) {
334
+ if(e.which == 13) {
335
+ insertLink();
336
+ insertLinkModal.modal('hide');
337
+ }
338
+ });
339
+
340
+ insertButton.click(insertLink);
341
+
342
+ insertLinkModal.on('shown', function() {
343
+ urlInput.focus();
344
+ });
345
+
346
+ insertLinkModal.on('hide', function() {
347
+ self.editor.currentView.element.focus();
348
+ });
349
+
350
+ toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
351
+ var activeButton = $(this).hasClass("wysihtml5-command-active");
352
+
353
+ if (!activeButton) {
354
+ self.editor.currentView.element.focus(false);
355
+ caretBookmark = self.editor.composer.selection.getBookmark();
356
+ insertLinkModal.appendTo('body').modal('show');
357
+ insertLinkModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
358
+ e.stopPropagation();
359
+ });
360
+ return false;
361
+ }
362
+ else {
363
+ return true;
364
+ }
365
+ });
366
+ }
367
+ };
368
+
369
+ // these define our public api
370
+ var methods = {
371
+ resetDefaults: function() {
372
+ $.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
373
+ },
374
+ bypassDefaults: function(options) {
375
+ return this.each(function () {
376
+ var $this = $(this);
377
+ $this.data('wysihtml5', new Wysihtml5($this, options));
378
+ });
379
+ },
380
+ shallowExtend: function (options) {
381
+ var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {});
382
+ var that = this;
383
+ return methods.bypassDefaults.apply(that, [settings]);
384
+ },
385
+ deepExtend: function(options) {
386
+ var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
387
+ var that = this;
388
+ return methods.bypassDefaults.apply(that, [settings]);
389
+ },
390
+ init: function(options) {
391
+ var that = this;
392
+ return methods.shallowExtend.apply(that, [options]);
393
+ }
394
+ };
395
+
396
+ $.fn.wysihtml5 = function ( method ) {
397
+ if ( methods[method] ) {
398
+ return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
399
+ } else if ( typeof method === 'object' || ! method ) {
400
+ return methods.init.apply( this, arguments );
401
+ } else {
402
+ $.error( 'Method ' + method + ' does not exist on jQuery.wysihtml5' );
403
+ }
404
+ };
405
+
406
+ $.fn.wysihtml5.Constructor = Wysihtml5;
407
+
408
+ var defaultOptions = $.fn.wysihtml5.defaultOptions = {
409
+ "font-styles": true,
410
+ "color": false,
411
+ "emphasis": true,
412
+ "lists": true,
413
+ "html": false,
414
+ "link": true,
415
+ "image": true,
416
+ customCommand: false,
417
+ events: {},
418
+ parserRules: {
419
+ classes: {
420
+ // (path_to_project/lib/css/wysiwyg-color.css)
421
+ "wysiwyg-color-silver" : 1,
422
+ "wysiwyg-color-gray" : 1,
423
+ "wysiwyg-color-white" : 1,
424
+ "wysiwyg-color-maroon" : 1,
425
+ "wysiwyg-color-red" : 1,
426
+ "wysiwyg-color-purple" : 1,
427
+ "wysiwyg-color-fuchsia" : 1,
428
+ "wysiwyg-color-green" : 1,
429
+ "wysiwyg-color-lime" : 1,
430
+ "wysiwyg-color-olive" : 1,
431
+ "wysiwyg-color-yellow" : 1,
432
+ "wysiwyg-color-navy" : 1,
433
+ "wysiwyg-color-blue" : 1,
434
+ "wysiwyg-color-teal" : 1,
435
+ "wysiwyg-color-aqua" : 1,
436
+ "wysiwyg-color-orange" : 1
437
+ },
438
+ tags: {
439
+ "b": {},
440
+ "i": {},
441
+ "br": {},
442
+ "ol": {},
443
+ "ul": {},
444
+ "li": {},
445
+ "h1": {},
446
+ "h2": {},
447
+ "h3": {},
448
+ "blockquote": {},
449
+ "u": 1,
450
+ "img": {
451
+ "check_attributes": {
452
+ "width": "numbers",
453
+ "alt": "alt",
454
+ "src": "url",
455
+ "height": "numbers"
456
+ }
457
+ },
458
+ "a": {
459
+ set_attributes: {
460
+ target: "_blank",
461
+ rel: "nofollow"
462
+ },
463
+ check_attributes: {
464
+ href: "url" // important to avoid XSS
465
+ }
466
+ },
467
+ "span": 1,
468
+ "div": 1,
469
+ // to allow save and edit files with code tag hacks
470
+ "code": 1,
471
+ "pre": 1
472
+ }
473
+ },
474
+ stylesheets: ["./lib/css/wysiwyg-color.css"], // (path_to_project/lib/css/wysiwyg-color.css)
475
+ locale: "en"
476
+ };
477
+
478
+ if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
479
+ $.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
480
+ }
481
+
482
+ var locale = $.fn.wysihtml5.locale = {
483
+ en: {
484
+ font_styles: {
485
+ title: "Font style",
486
+ normal: "Normal text",
487
+ h1: "Heading 1",
488
+ h2: "Heading 2",
489
+ h3: "Heading 3"
490
+ },
491
+ emphasis: {
492
+ bold: "Bold",
493
+ italic: "Italic",
494
+ underline: "Underline"
495
+ },
496
+ lists: {
497
+ unordered: "Unordered list",
498
+ ordered: "Ordered list",
499
+ outdent: "Outdent",
500
+ indent: "Indent"
501
+ },
502
+ link: {
503
+ insert: "Insert link",
504
+ cancel: "Cancel"
505
+ },
506
+ image: {
507
+ insert: "Insert image",
508
+ cancel: "Cancel"
509
+ },
510
+ html: {
511
+ edit: "Edit HTML"
512
+ },
513
+ colours: {
514
+ title: "Text color",
515
+ black: "Black",
516
+ silver: "Silver",
517
+ gray: "Grey",
518
+ maroon: "Maroon",
519
+ red: "Red",
520
+ purple: "Purple",
521
+ green: "Green",
522
+ olive: "Olive",
523
+ navy: "Navy",
524
+ blue: "Blue",
525
+ orange: "Orange"
526
+ }
527
+ }
528
+ };
529
+
530
+ }(window.jQuery, window.wysihtml5);