lato_view 1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (231) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +35 -0
  3. data/app/assets/fonts/lato-bold-webfont.eot +0 -0
  4. data/app/assets/fonts/lato-bold-webfont.svg +4551 -0
  5. data/app/assets/fonts/lato-bold-webfont.ttf +0 -0
  6. data/app/assets/fonts/lato-bold-webfont.woff +0 -0
  7. data/app/assets/fonts/lato-bold-webfont.woff2 +0 -0
  8. data/app/assets/fonts/lato-regular-webfont.eot +0 -0
  9. data/app/assets/fonts/lato-regular-webfont.svg +4241 -0
  10. data/app/assets/fonts/lato-regular-webfont.ttf +0 -0
  11. data/app/assets/fonts/lato-regular-webfont.woff +0 -0
  12. data/app/assets/fonts/lato-regular-webfont.woff2 +0 -0
  13. data/app/assets/images/lato_view/icons/_arrow-down.svg +1 -0
  14. data/app/assets/images/lato_view/icons/_arrow-left.svg +1 -0
  15. data/app/assets/images/lato_view/icons/_arrow-right.svg +1 -0
  16. data/app/assets/images/lato_view/icons/_arrow-up.svg +1 -0
  17. data/app/assets/images/lato_view/icons/_artist.svg +1 -0
  18. data/app/assets/images/lato_view/icons/_artwork.svg +1 -0
  19. data/app/assets/images/lato_view/icons/_attachment.svg +1 -0
  20. data/app/assets/images/lato_view/icons/_calendar.svg +1 -0
  21. data/app/assets/images/lato_view/icons/_clients.svg +1 -0
  22. data/app/assets/images/lato_view/icons/_close.svg +1 -0
  23. data/app/assets/images/lato_view/icons/_download.svg +1 -0
  24. data/app/assets/images/lato_view/icons/_file.svg +1 -0
  25. data/app/assets/images/lato_view/icons/_google-plus.svg +1 -0
  26. data/app/assets/images/lato_view/icons/_home.svg +1 -0
  27. data/app/assets/images/lato_view/icons/_info.svg +1 -0
  28. data/app/assets/images/lato_view/icons/_instagram.svg +1 -0
  29. data/app/assets/images/lato_view/icons/_lato-logo.svg +1 -0
  30. data/app/assets/images/lato_view/icons/_linkedin.svg +1 -0
  31. data/app/assets/images/lato_view/icons/_list.svg +1 -0
  32. data/app/assets/images/lato_view/icons/_magni-glass.svg +1 -0
  33. data/app/assets/images/lato_view/icons/_mail.svg +1 -0
  34. data/app/assets/images/lato_view/icons/_minus.svg +1 -0
  35. data/app/assets/images/lato_view/icons/_modify.svg +1 -0
  36. data/app/assets/images/lato_view/icons/_percentage.svg +1 -0
  37. data/app/assets/images/lato_view/icons/_plus.svg +1 -0
  38. data/app/assets/images/lato_view/icons/_power.svg +1 -0
  39. data/app/assets/images/lato_view/icons/_settings.svg +1 -0
  40. data/app/assets/images/lato_view/icons/_sweet.svg +1 -0
  41. data/app/assets/images/lato_view/icons/_trash.svg +1 -0
  42. data/app/assets/images/lato_view/icons/_twitter.svg +1 -0
  43. data/app/assets/images/lato_view/icons/_upload.svg +1 -0
  44. data/app/assets/images/lato_view/icons/_user.svg +1 -0
  45. data/app/assets/images/lato_view/icons/_users.svg +1 -0
  46. data/app/assets/images/lato_view/icons/_warning.svg +1 -0
  47. data/app/assets/images/lato_view/icons/_web.svg +1 -0
  48. data/app/assets/images/lato_view/vendor/trumbowyg_icons.svg +1 -0
  49. data/app/assets/images/lato_view/work-in-progress/da-scegliere.svg +225 -0
  50. data/app/assets/images/lato_view/work-in-progress/social.svg +76 -0
  51. data/app/assets/javascripts/lato_view/application.js +68 -0
  52. data/app/assets/javascripts/lato_view/build/ActionBar.js +65 -0
  53. data/app/assets/javascripts/lato_view/build/Dropdown.js +33 -0
  54. data/app/assets/javascripts/lato_view/build/Flash.js +23 -0
  55. data/app/assets/javascripts/lato_view/build/FormManager.js +159 -0
  56. data/app/assets/javascripts/lato_view/build/Navigation.js +79 -0
  57. data/app/assets/javascripts/lato_view/build/Util.js +91 -0
  58. data/app/assets/javascripts/lato_view/build/Validator.js +235 -0
  59. data/app/assets/javascripts/lato_view/vendor/dropzone.min.js +2 -0
  60. data/app/assets/javascripts/lato_view/vendor/jquery.email-autocomplete.min.js +9 -0
  61. data/app/assets/javascripts/lato_view/vendor/modernizr.js +3 -0
  62. data/app/assets/javascripts/lato_view/vendor/moment.min.js +7 -0
  63. data/app/assets/javascripts/lato_view/vendor/picker-it_IT.js +1 -0
  64. data/app/assets/javascripts/lato_view/vendor/picker.date.js +5 -0
  65. data/app/assets/javascripts/lato_view/vendor/picker.js +7 -0
  66. data/app/assets/javascripts/lato_view/vendor/picker.time.js +5 -0
  67. data/app/assets/javascripts/lato_view/vendor/trumbowyg.js +1522 -0
  68. data/app/assets/stylesheets/lato_view/application.scss.erb +32 -0
  69. data/app/assets/stylesheets/lato_view/base/_base.scss +53 -0
  70. data/app/assets/stylesheets/lato_view/base/_helpers.scss +491 -0
  71. data/app/assets/stylesheets/lato_view/base/_media.scss +32 -0
  72. data/app/assets/stylesheets/lato_view/base/_reset.scss +424 -0
  73. data/app/assets/stylesheets/lato_view/base/_typography.scss +138 -0
  74. data/app/assets/stylesheets/lato_view/config/_config.scss +143 -0
  75. data/app/assets/stylesheets/lato_view/config/templates/_base-template.scss +158 -0
  76. data/app/assets/stylesheets/lato_view/config/templates/_black-template.scss +158 -0
  77. data/app/assets/stylesheets/lato_view/modules/_action-bar.scss +62 -0
  78. data/app/assets/stylesheets/lato_view/modules/_arranger.scss +10 -0
  79. data/app/assets/stylesheets/lato_view/modules/_button-group.scss +70 -0
  80. data/app/assets/stylesheets/lato_view/modules/_buttons.scss +177 -0
  81. data/app/assets/stylesheets/lato_view/modules/_content-block.scss +33 -0
  82. data/app/assets/stylesheets/lato_view/modules/_datepicker.scss +580 -0
  83. data/app/assets/stylesheets/lato_view/modules/_dropdown.scss +166 -0
  84. data/app/assets/stylesheets/lato_view/modules/_flash.scss +55 -0
  85. data/app/assets/stylesheets/lato_view/modules/_form-controls.scss +83 -0
  86. data/app/assets/stylesheets/lato_view/modules/_form.scss +297 -0
  87. data/app/assets/stylesheets/lato_view/modules/_modules.scss +19 -0
  88. data/app/assets/stylesheets/lato_view/modules/_navbar.scss +262 -0
  89. data/app/assets/stylesheets/lato_view/modules/_pagination.scss +30 -0
  90. data/app/assets/stylesheets/lato_view/modules/_search-bar.scss +44 -0
  91. data/app/assets/stylesheets/lato_view/modules/_select.scss +313 -0
  92. data/app/assets/stylesheets/lato_view/modules/_sidebar.scss +211 -0
  93. data/app/assets/stylesheets/lato_view/modules/_status.scss +39 -0
  94. data/app/assets/stylesheets/lato_view/modules/_table.scss +94 -0
  95. data/app/assets/stylesheets/lato_view/modules/_upload.scss +371 -0
  96. data/app/assets/stylesheets/lato_view/modules/_wyswyg.scss +755 -0
  97. data/app/assets/stylesheets/lato_view/views/_admin.scss +30 -0
  98. data/app/assets/stylesheets/lato_view/views/_grid.scss +448 -0
  99. data/app/assets/stylesheets/lato_view/views/_login.scss +152 -0
  100. data/app/assets/stylesheets/lato_view/views/_print.scss +41 -0
  101. data/app/assets/stylesheets/lato_view/views/_views.scss +48 -0
  102. data/app/concepts/lato_view/actionbar/cell.rb +65 -0
  103. data/app/concepts/lato_view/actionbar/views/show.html.erb +21 -0
  104. data/app/concepts/lato_view/block/cell.rb +52 -0
  105. data/app/concepts/lato_view/buttongroup/cell.rb +50 -0
  106. data/app/concepts/lato_view/buttongroup/views/show.html.erb +7 -0
  107. data/app/concepts/lato_view/dropdown/cell.rb +46 -0
  108. data/app/concepts/lato_view/dropdown/views/show.html.erb +19 -0
  109. data/app/concepts/lato_view/index/cell.rb +112 -0
  110. data/app/concepts/lato_view/index/views/head.html.erb +12 -0
  111. data/app/concepts/lato_view/index/views/rows.html.erb +49 -0
  112. data/app/concepts/lato_view/input/cell.rb +148 -0
  113. data/app/concepts/lato_view/input/views/checkbox.html.erb +8 -0
  114. data/app/concepts/lato_view/input/views/date.html.erb +5 -0
  115. data/app/concepts/lato_view/input/views/editor.html.erb +7 -0
  116. data/app/concepts/lato_view/input/views/email.html.erb +5 -0
  117. data/app/concepts/lato_view/input/views/file.html.erb +17 -0
  118. data/app/concepts/lato_view/input/views/multiple-select.html.erb +12 -0
  119. data/app/concepts/lato_view/input/views/number.html.erb +5 -0
  120. data/app/concepts/lato_view/input/views/password.html.erb +8 -0
  121. data/app/concepts/lato_view/input/views/radio.html.erb +10 -0
  122. data/app/concepts/lato_view/input/views/select.html.erb +12 -0
  123. data/app/concepts/lato_view/input/views/text.html.erb +5 -0
  124. data/app/concepts/lato_view/input/views/textarea.html.erb +5 -0
  125. data/app/concepts/lato_view/input/views/time.html.erb +5 -0
  126. data/app/concepts/lato_view/searchbar/cell.rb +63 -0
  127. data/app/concepts/lato_view/searchbar/views/show.html.erb +6 -0
  128. data/app/concepts/lato_view/table/cell.rb +88 -0
  129. data/app/concepts/lato_view/table/views/head.html.erb +9 -0
  130. data/app/concepts/lato_view/table/views/row.html.erb +7 -0
  131. data/app/concepts/lato_view/table/views/rows.html.erb +5 -0
  132. data/app/controllers/lato_view/assets_controller.rb +18 -0
  133. data/app/controllers/lato_view/develop_controller.rb +18 -0
  134. data/app/helpers/lato_view/application_helper.rb +22 -0
  135. data/app/views/lato_view/develop/home.html.erb +281 -0
  136. data/app/views/lato_view/icons/_arrowdown.svg +1 -0
  137. data/app/views/lato_view/icons/_arrowleft.svg +1 -0
  138. data/app/views/lato_view/icons/_arrowright.svg +1 -0
  139. data/app/views/lato_view/icons/_arrowup.svg +1 -0
  140. data/app/views/lato_view/icons/_artist.svg +1 -0
  141. data/app/views/lato_view/icons/_artwork.svg +1 -0
  142. data/app/views/lato_view/icons/_attachment.svg +1 -0
  143. data/app/views/lato_view/icons/_calendar.svg +1 -0
  144. data/app/views/lato_view/icons/_clients.svg +1 -0
  145. data/app/views/lato_view/icons/_close.svg +1 -0
  146. data/app/views/lato_view/icons/_download.svg +1 -0
  147. data/app/views/lato_view/icons/_file.svg +1 -0
  148. data/app/views/lato_view/icons/_googleplus.svg +1 -0
  149. data/app/views/lato_view/icons/_home.svg +1 -0
  150. data/app/views/lato_view/icons/_info.svg +1 -0
  151. data/app/views/lato_view/icons/_instagram.svg +1 -0
  152. data/app/views/lato_view/icons/_latologo.svg +1 -0
  153. data/app/views/lato_view/icons/_linkedin.svg +1 -0
  154. data/app/views/lato_view/icons/_list.svg +1 -0
  155. data/app/views/lato_view/icons/_magni-glass.svg +1 -0
  156. data/app/views/lato_view/icons/_mail.svg +1 -0
  157. data/app/views/lato_view/icons/_minus.svg +1 -0
  158. data/app/views/lato_view/icons/_modify.svg +1 -0
  159. data/app/views/lato_view/icons/_percentage.svg +1 -0
  160. data/app/views/lato_view/icons/_plus.svg +1 -0
  161. data/app/views/lato_view/icons/_power.svg +1 -0
  162. data/app/views/lato_view/icons/_settings.svg +1 -0
  163. data/app/views/lato_view/icons/_sweet.svg +1 -0
  164. data/app/views/lato_view/icons/_trash.svg +1 -0
  165. data/app/views/lato_view/icons/_twitter.svg +1 -0
  166. data/app/views/lato_view/icons/_upload.svg +1 -0
  167. data/app/views/lato_view/icons/_user.svg +1 -0
  168. data/app/views/lato_view/icons/_users.svg +1 -0
  169. data/app/views/lato_view/icons/_warning.svg +1 -0
  170. data/app/views/lato_view/icons/_web.svg +1 -0
  171. data/app/views/lato_view/layout/_flash.html.erb +5 -0
  172. data/app/views/lato_view/layout/_header.html.erb +31 -0
  173. data/app/views/lato_view/layout/_sidebar.html.erb +28 -0
  174. data/app/views/lato_view/layout/_strings.html.erb +8 -0
  175. data/app/views/layouts/lato_layout.html.erb +81 -0
  176. data/config/example.yml +19 -0
  177. data/config/initializers/assets.rb +5 -0
  178. data/config/initializers/init.rb +5 -0
  179. data/config/initializers/lists.rb +34 -0
  180. data/config/initializers/ram.rb +23 -0
  181. data/config/routes.rb +9 -0
  182. data/lib/lato_view/concepts.rb +19 -0
  183. data/lib/lato_view/engine.rb +23 -0
  184. data/lib/lato_view/interface/assets.rb +90 -0
  185. data/lib/lato_view/interface/images.rb +69 -0
  186. data/lib/lato_view/interface/navigation.rb +17 -0
  187. data/lib/lato_view/interface/themes.rb +37 -0
  188. data/lib/lato_view/interface.rb +19 -0
  189. data/lib/lato_view.rb +20 -0
  190. data/lib/tasks/lato_starter_tasks.rake +11 -0
  191. data/test/controllers/lato_view/api/v1/api_controller_test.rb +9 -0
  192. data/test/controllers/lato_view/application_controller_test.rb +9 -0
  193. data/test/controllers/lato_view/back/back_controller_test.rb +9 -0
  194. data/test/dummy/README.rdoc +28 -0
  195. data/test/dummy/Rakefile +6 -0
  196. data/test/dummy/app/assets/javascripts/application.js +13 -0
  197. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  198. data/test/dummy/app/controllers/application_controller.rb +5 -0
  199. data/test/dummy/app/helpers/application_helper.rb +2 -0
  200. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  201. data/test/dummy/bin/bundle +3 -0
  202. data/test/dummy/bin/rails +4 -0
  203. data/test/dummy/bin/rake +4 -0
  204. data/test/dummy/bin/setup +29 -0
  205. data/test/dummy/config/application.rb +25 -0
  206. data/test/dummy/config/boot.rb +5 -0
  207. data/test/dummy/config/database.yml +25 -0
  208. data/test/dummy/config/environment.rb +5 -0
  209. data/test/dummy/config/environments/development.rb +41 -0
  210. data/test/dummy/config/environments/production.rb +79 -0
  211. data/test/dummy/config/environments/test.rb +42 -0
  212. data/test/dummy/config/initializers/assets.rb +11 -0
  213. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  214. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  215. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  216. data/test/dummy/config/initializers/inflections.rb +16 -0
  217. data/test/dummy/config/initializers/mime_types.rb +4 -0
  218. data/test/dummy/config/initializers/session_store.rb +3 -0
  219. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  220. data/test/dummy/config/locales/en.yml +23 -0
  221. data/test/dummy/config/routes.rb +3 -0
  222. data/test/dummy/config/secrets.yml +22 -0
  223. data/test/dummy/config.ru +4 -0
  224. data/test/dummy/public/404.html +67 -0
  225. data/test/dummy/public/422.html +67 -0
  226. data/test/dummy/public/500.html +66 -0
  227. data/test/dummy/public/favicon.ico +0 -0
  228. data/test/integration/navigation_test.rb +8 -0
  229. data/test/lato_view_test.rb +7 -0
  230. data/test/test_helper.rb +21 -0
  231. metadata +453 -0
@@ -0,0 +1,1522 @@
1
+ jQuery.trumbowyg = {
2
+ langs: {
3
+ en: {
4
+ viewHTML: 'View HTML',
5
+
6
+ undo: 'Undo',
7
+ redo: 'Redo',
8
+
9
+ formatting: 'Formatting',
10
+ p: 'Paragraph',
11
+ blockquote: 'Quote',
12
+ code: 'Code',
13
+ header: 'Header',
14
+
15
+ bold: 'Bold',
16
+ italic: 'Italic',
17
+ strikethrough: 'Stroke',
18
+ underline: 'Underline',
19
+
20
+ strong: 'Strong',
21
+ em: 'Emphasis',
22
+ del: 'Deleted',
23
+
24
+ superscript: 'Superscript',
25
+ subscript: 'Subscript',
26
+
27
+ unorderedList: 'Unordered list',
28
+ orderedList: 'Ordered list',
29
+
30
+ insertImage: 'Insert Image',
31
+ link: 'Link',
32
+ createLink: 'Insert link',
33
+ unlink: 'Remove link',
34
+
35
+ justifyLeft: 'Align Left',
36
+ justifyCenter: 'Align Center',
37
+ justifyRight: 'Align Right',
38
+ justifyFull: 'Align Justify',
39
+
40
+ horizontalRule: 'Insert horizontal rule',
41
+ removeformat: 'Remove format',
42
+
43
+ fullscreen: 'Fullscreen',
44
+
45
+ close: 'Close',
46
+
47
+ submit: 'Confirm',
48
+ reset: 'Cancel',
49
+
50
+ required: 'Required',
51
+ description: 'Description',
52
+ title: 'Title',
53
+ text: 'Text',
54
+ target: 'Target'
55
+ }
56
+ },
57
+
58
+ // Plugins
59
+ plugins: {},
60
+
61
+ // SVG Path globally
62
+ svgPath: '/lato/view/vendor/trumbowyg_icons'
63
+ };
64
+
65
+
66
+ (function (navigator, window, document, $) {
67
+ 'use strict';
68
+
69
+ $.fn.trumbowyg = function (options, params) {
70
+ var trumbowygDataName = 'trumbowyg';
71
+ if (options === Object(options) || !options) {
72
+ return this.each(function () {
73
+ if (!$(this).data(trumbowygDataName)) {
74
+ $(this).data(trumbowygDataName, new Trumbowyg(this, options));
75
+ }
76
+ });
77
+ }
78
+ if (this.length === 1) {
79
+ try {
80
+ var t = $(this).data(trumbowygDataName);
81
+ switch (options) {
82
+ // Exec command
83
+ case 'execCmd':
84
+ return t.execCmd(params.cmd, params.param, params.forceCss);
85
+
86
+ // Modal box
87
+ case 'openModal':
88
+ return t.openModal(params.title, params.content);
89
+ case 'closeModal':
90
+ return t.closeModal();
91
+ case 'openModalInsert':
92
+ return t.openModalInsert(params.title, params.fields, params.callback);
93
+
94
+ // Range
95
+ case 'saveRange':
96
+ return t.saveRange();
97
+ case 'getRange':
98
+ return t.range;
99
+ case 'getRangeText':
100
+ return t.getRangeText();
101
+ case 'restoreRange':
102
+ return t.restoreRange();
103
+
104
+ // Enable/disable
105
+ case 'enable':
106
+ return t.toggleDisable(false);
107
+ case 'disable':
108
+ return t.toggleDisable(true);
109
+
110
+ // Destroy
111
+ case 'destroy':
112
+ return t.destroy();
113
+
114
+ // Empty
115
+ case 'empty':
116
+ return t.empty();
117
+
118
+ // HTML
119
+ case 'html':
120
+ return t.html(params);
121
+ }
122
+ } catch (c) {
123
+ }
124
+ }
125
+
126
+ return false;
127
+ };
128
+
129
+ // @param: editorElem is the DOM element
130
+ var Trumbowyg = function (editorElem, options) {
131
+ var t = this,
132
+ trumbowygIconsId = 'trumbowyg-icons';
133
+
134
+ // Get the document of the element. It use to makes the plugin
135
+ // compatible on iframes.
136
+ t.doc = editorElem.ownerDocument || document;
137
+
138
+ // jQuery object of the editor
139
+ t.$ta = $(editorElem); // $ta : Textarea
140
+ t.$c = $(editorElem); // $c : creator
141
+
142
+ options = options || {};
143
+
144
+ // Localization management
145
+ if (options.lang != null || $.trumbowyg.langs[options.lang] != null) {
146
+ t.lang = $.extend(true, {}, $.trumbowyg.langs.en, $.trumbowyg.langs[options.lang]);
147
+ } else {
148
+ t.lang = $.trumbowyg.langs.en;
149
+ }
150
+
151
+ // SVG path
152
+ var svgPathOption = $.trumbowyg.svgPath != null ? $.trumbowyg.svgPath : options.svgPath;
153
+ t.hasSvg = svgPathOption !== false;
154
+ t.svgPath = !!t.doc.querySelector('base') ? window.location : '';
155
+ if ($('#' + trumbowygIconsId, t.doc).length === 0 && svgPathOption !== false) {
156
+ if (svgPathOption == null) {
157
+ try {
158
+ throw new Error();
159
+ } catch (e) {
160
+ var stackLines = e.stack.split('\n');
161
+
162
+ for (var i in stackLines) {
163
+ if (!stackLines[i].match(/http[s]?:\/\//)) {
164
+ continue;
165
+ }
166
+ svgPathOption = stackLines[Number(i)].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/)[1].split('/');
167
+ svgPathOption.pop();
168
+ svgPathOption = svgPathOption.join('/') + '/ui/icons.svg';
169
+ break;
170
+ }
171
+ }
172
+ }
173
+
174
+ var div = t.doc.createElement('div');
175
+ div.id = trumbowygIconsId;
176
+ t.doc.body.insertBefore(div, t.doc.body.childNodes[0]);
177
+ $.get(svgPathOption, function (data) {
178
+ div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
179
+ });
180
+ }
181
+
182
+
183
+ /**
184
+ * When the button is associated to a empty object
185
+ * fn and title attributs are defined from the button key value
186
+ *
187
+ * For example
188
+ * foo: {}
189
+ * is equivalent to :
190
+ * foo: {
191
+ * fn: 'foo',
192
+ * title: this.lang.foo
193
+ * }
194
+ */
195
+ var h = t.lang.header, // Header translation
196
+ isBlinkFunction = function () {
197
+ return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window;
198
+ };
199
+ t.btnsDef = {
200
+ viewHTML: {
201
+ fn: 'toggle'
202
+ },
203
+
204
+ undo: {
205
+ isSupported: isBlinkFunction,
206
+ key: 'Z'
207
+ },
208
+ redo: {
209
+ isSupported: isBlinkFunction,
210
+ key: 'Y'
211
+ },
212
+
213
+ p: {
214
+ fn: 'formatBlock'
215
+ },
216
+ blockquote: {
217
+ fn: 'formatBlock'
218
+ },
219
+ h1: {
220
+ fn: 'formatBlock',
221
+ title: h + ' 1'
222
+ },
223
+ h2: {
224
+ fn: 'formatBlock',
225
+ title: h + ' 2'
226
+ },
227
+ h3: {
228
+ fn: 'formatBlock',
229
+ title: h + ' 3'
230
+ },
231
+ h4: {
232
+ fn: 'formatBlock',
233
+ title: h + ' 4'
234
+ },
235
+ subscript: {
236
+ tag: 'sub'
237
+ },
238
+ superscript: {
239
+ tag: 'sup'
240
+ },
241
+
242
+ bold: {
243
+ key: 'B'
244
+ },
245
+ italic: {
246
+ key: 'I'
247
+ },
248
+ underline: {
249
+ tag: 'u'
250
+ },
251
+ strikethrough: {
252
+ tag: 'strike'
253
+ },
254
+
255
+ strong: {
256
+ fn: 'bold',
257
+ key: 'B'
258
+ },
259
+ em: {
260
+ fn: 'italic',
261
+ key: 'I'
262
+ },
263
+ del: {
264
+ fn: 'strikethrough'
265
+ },
266
+
267
+ createLink: {
268
+ key: 'K',
269
+ tag: 'a'
270
+ },
271
+ unlink: {},
272
+
273
+ insertImage: {},
274
+
275
+ justifyLeft: {
276
+ tag: 'left',
277
+ forceCss: true
278
+ },
279
+ justifyCenter: {
280
+ tag: 'center',
281
+ forceCss: true
282
+ },
283
+ justifyRight: {
284
+ tag: 'right',
285
+ forceCss: true
286
+ },
287
+ justifyFull: {
288
+ tag: 'justify',
289
+ forceCss: true
290
+ },
291
+
292
+ unorderedList: {
293
+ fn: 'insertUnorderedList',
294
+ tag: 'ul'
295
+ },
296
+ orderedList: {
297
+ fn: 'insertOrderedList',
298
+ tag: 'ol'
299
+ },
300
+
301
+ horizontalRule: {
302
+ fn: 'insertHorizontalRule'
303
+ },
304
+
305
+ removeformat: {},
306
+
307
+ fullscreen: {
308
+ class: 'trumbowyg-not-disable'
309
+ },
310
+ close: {
311
+ fn: 'destroy',
312
+ class: 'trumbowyg-not-disable'
313
+ },
314
+
315
+ // Dropdowns
316
+ formatting: {
317
+ dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'],
318
+ ico: 'p'
319
+ },
320
+ link: {
321
+ dropdown: ['createLink', 'unlink']
322
+ }
323
+ };
324
+
325
+ // Defaults Options
326
+ t.o = $.extend(true, {}, {
327
+ lang: 'en',
328
+
329
+ fixedBtnPane: false,
330
+ fixedFullWidth: false,
331
+ autogrow: false,
332
+
333
+ prefix: 'trumbowyg-',
334
+
335
+ semantic: true,
336
+ resetCss: false,
337
+ removeformatPasted: false,
338
+ tagsToRemove: [],
339
+
340
+ btnsGrps: {
341
+ design: ['bold', 'italic', 'underline', 'strikethrough'],
342
+ semantic: ['strong', 'em', 'del'],
343
+ justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
344
+ lists: ['unorderedList', 'orderedList']
345
+ },
346
+ btns: [
347
+ ['viewHTML'],
348
+ ['undo', 'redo'],
349
+ ['formatting'],
350
+ 'btnGrp-semantic',
351
+ ['superscript', 'subscript'],
352
+ ['link'],
353
+ ['insertImage'],
354
+ 'btnGrp-justify',
355
+ 'btnGrp-lists',
356
+ ['horizontalRule'],
357
+ ['removeformat'],
358
+ ['fullscreen']
359
+ ],
360
+ // For custom button definitions
361
+ btnsDef: {},
362
+
363
+ inlineElementsSelector: 'a,abbr,acronym,b,caption,cite,code,col,dfn,dir,dt,dd,em,font,hr,i,kbd,li,q,span,strikeout,strong,sub,sup,u',
364
+
365
+ pasteHandlers: [],
366
+
367
+ imgDblClickHandler: function () {
368
+ var $img = $(this),
369
+ src = $img.attr('src'),
370
+ base64 = '(Base64)';
371
+
372
+ if (src.indexOf('data:image') === 0) {
373
+ src = base64;
374
+ }
375
+
376
+ t.openModalInsert(t.lang.insertImage, {
377
+ url: {
378
+ label: 'URL',
379
+ value: src,
380
+ required: true
381
+ },
382
+ alt: {
383
+ label: t.lang.description,
384
+ value: $img.attr('alt')
385
+ }
386
+ }, function (v) {
387
+ if (v.src !== base64) {
388
+ $img.attr({
389
+ src: v.src
390
+ });
391
+ }
392
+ $img.attr({
393
+ alt: v.alt
394
+ });
395
+ return true;
396
+ });
397
+ return false;
398
+ },
399
+
400
+ plugins: {}
401
+ }, options);
402
+
403
+ t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled);
404
+
405
+ if (options.btns) {
406
+ t.o.btns = options.btns;
407
+ } else if (!t.o.semantic) {
408
+ t.o.btns[4] = 'btnGrp-design';
409
+ }
410
+
411
+ $.each(t.o.btnsDef, function (btnName, btnDef) {
412
+ t.addBtnDef(btnName, btnDef);
413
+ });
414
+
415
+ // Keyboard shortcuts are load in this array
416
+ t.keys = [];
417
+
418
+ // Tag to button dynamically hydrated
419
+ t.tagToButton = {};
420
+ t.tagHandlers = [];
421
+
422
+ // Admit multiple paste handlers
423
+ t.pasteHandlers = [].concat(t.o.pasteHandlers);
424
+
425
+ t.init();
426
+ };
427
+
428
+ Trumbowyg.prototype = {
429
+ init: function () {
430
+ var t = this;
431
+ t.height = t.$ta.height();
432
+
433
+ t.initPlugins();
434
+
435
+ // Disable image resize in Firefox
436
+ t.doc.execCommand('enableObjectResizing', false, false);
437
+ t.doc.execCommand('defaultParagraphSeparator', false, 'p');
438
+
439
+ t.buildEditor();
440
+ t.buildBtnPane();
441
+
442
+ t.fixedBtnPaneEvents();
443
+
444
+ t.buildOverlay();
445
+
446
+ setTimeout(function () {
447
+ if (t.disabled) {
448
+ t.toggleDisable(true);
449
+ }
450
+ t.$c.trigger('tbwinit');
451
+ });
452
+ },
453
+
454
+ addBtnDef: function (btnName, btnDef) {
455
+ this.btnsDef[btnName] = btnDef;
456
+ },
457
+
458
+ buildEditor: function () {
459
+ var t = this,
460
+ prefix = t.o.prefix,
461
+ html = '';
462
+
463
+ t.$box = $('<div/>', {
464
+ class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg'
465
+ });
466
+
467
+ // $ta = Textarea
468
+ // $ed = Editor
469
+ t.isTextarea = t.$ta.is('textarea');
470
+ if (t.isTextarea) {
471
+ html = t.$ta.val();
472
+ t.$ed = $('<div/>');
473
+ t.$box
474
+ .insertAfter(t.$ta)
475
+ .append(t.$ed, t.$ta);
476
+ } else {
477
+ t.$ed = t.$ta;
478
+ html = t.$ed.html();
479
+
480
+ t.$ta = $('<textarea/>', {
481
+ name: t.$ta.attr('id'),
482
+ height: t.height
483
+ }).val(html);
484
+
485
+ t.$box
486
+ .insertAfter(t.$ed)
487
+ .append(t.$ta, t.$ed);
488
+ t.syncCode();
489
+ }
490
+
491
+ t.$ta
492
+ .addClass(prefix + 'textarea')
493
+ .attr('tabindex', -1)
494
+ ;
495
+
496
+ t.$ed
497
+ .addClass(prefix + 'editor')
498
+ .attr({
499
+ contenteditable: true,
500
+ dir: t.lang._dir || 'ltr'
501
+ })
502
+ .html(html)
503
+ ;
504
+
505
+ if (t.o.tabindex) {
506
+ t.$ed.attr('tabindex', t.o.tabindex);
507
+ }
508
+
509
+ if (t.$c.is('[placeholder]')) {
510
+ t.$ed.attr('placeholder', t.$c.attr('placeholder'));
511
+ }
512
+
513
+ if (t.o.resetCss) {
514
+ t.$ed.addClass(prefix + 'reset-css');
515
+ }
516
+
517
+ if (!t.o.autogrow) {
518
+ t.$ta.add(t.$ed).css({
519
+ height: t.height
520
+ });
521
+ }
522
+
523
+ t.semanticCode();
524
+
525
+
526
+ t._ctrl = false;
527
+ t.$ed
528
+ .on('dblclick', 'img', t.o.imgDblClickHandler)
529
+ .on('keydown', function (e) {
530
+ t._composition = (e.which === 229);
531
+
532
+ if (e.ctrlKey) {
533
+ t._ctrl = true;
534
+ var k = t.keys[String.fromCharCode(e.which).toUpperCase()];
535
+
536
+ try {
537
+ t.execCmd(k.fn, k.param);
538
+ return false;
539
+ } catch (c) {
540
+ }
541
+ }
542
+ })
543
+ .on('keyup', function (e) {
544
+ if (e.which >= 37 && e.which <= 40) {
545
+ return;
546
+ }
547
+
548
+ if (e.ctrlKey && (e.which === 89 || e.which === 90)) {
549
+ t.$c.trigger('tbwchange');
550
+ } else if (!t._ctrl && e.which !== 17 && !t._composition) {
551
+ t.semanticCode(false, e.which === 13);
552
+ t.$c.trigger('tbwchange');
553
+ }
554
+
555
+ setTimeout(function () {
556
+ t._ctrl = false;
557
+ }, 200);
558
+ })
559
+ .on('mouseup keydown keyup', function () {
560
+ t.updateButtonPaneStatus();
561
+ })
562
+ .on('focus blur', function (e) {
563
+ t.$c.trigger('tbw' + e.type);
564
+ if (e.type === 'blur') {
565
+ $('.' + prefix + 'active-button', t.$btnPane).removeClass(prefix + 'active-button ' + prefix + 'active');
566
+ }
567
+ })
568
+ .on('cut', function () {
569
+ t.$c.trigger('tbwchange');
570
+ })
571
+ .on('paste', function (e) {
572
+ if (t.o.removeformatPasted) {
573
+ e.preventDefault();
574
+
575
+ try {
576
+ // IE
577
+ var text = window.clipboardData.getData('Text');
578
+
579
+ try {
580
+ // <= IE10
581
+ t.doc.selection.createRange().pasteHTML(text);
582
+ } catch (c) {
583
+ // IE 11
584
+ t.doc.getSelection().getRangeAt(0).insertNode(t.doc.createTextNode(text));
585
+ }
586
+ } catch (d) {
587
+ // Not IE
588
+ t.execCmd('insertText', (e.originalEvent || e).clipboardData.getData('text/plain'));
589
+ }
590
+ }
591
+
592
+ // Call pasteHandlers
593
+ $.each(t.pasteHandlers, function (i, pasteHandler) {
594
+ pasteHandler(e);
595
+ });
596
+
597
+ setTimeout(function () {
598
+ if (t.o.semantic) {
599
+ t.semanticCode(false, true);
600
+ } else {
601
+ t.syncCode();
602
+ }
603
+ t.$c.trigger('tbwpaste', e);
604
+ }, 0);
605
+ });
606
+ t.$ta.on('keyup paste', function () {
607
+ t.$c.trigger('tbwchange');
608
+ });
609
+
610
+ $(t.doc).on('keydown', function (e) {
611
+ if (e.which === 27) {
612
+ t.closeModal();
613
+ return false;
614
+ }
615
+ });
616
+ },
617
+
618
+
619
+ // Build button pane, use o.btns option
620
+ buildBtnPane: function () {
621
+ var t = this,
622
+ prefix = t.o.prefix;
623
+
624
+ var $btnPane = t.$btnPane = $('<div/>', {
625
+ class: prefix + 'button-pane'
626
+ });
627
+
628
+ $.each(t.o.btns, function (i, btnGrps) {
629
+ // Managment of group of buttons
630
+ try {
631
+ var b = btnGrps.split('btnGrp-');
632
+ if (b[1] != null) {
633
+ btnGrps = t.o.btnsGrps[b[1]];
634
+ }
635
+ } catch (c) {
636
+ }
637
+
638
+ if (!$.isArray(btnGrps)) {
639
+ btnGrps = [btnGrps];
640
+ }
641
+
642
+ var $btnGroup = $('<div/>', {
643
+ class: prefix + 'button-group ' + ((btnGrps.indexOf('fullscreen') >= 0) ? prefix + 'right' : '')
644
+ });
645
+ $.each(btnGrps, function (i, btn) {
646
+ try { // Prevent buildBtn error
647
+ var $item;
648
+
649
+ if (t.isSupportedBtn(btn)) { // It's a supported button
650
+ $item = t.buildBtn(btn);
651
+ }
652
+
653
+ $btnGroup.append($item);
654
+ } catch (c) {
655
+ }
656
+ });
657
+ $btnPane.append($btnGroup);
658
+ });
659
+
660
+ t.$box.prepend($btnPane);
661
+ },
662
+
663
+
664
+ // Build a button and his action
665
+ buildBtn: function (btnName) { // btnName is name of the button
666
+ var t = this,
667
+ prefix = t.o.prefix,
668
+ btn = t.btnsDef[btnName],
669
+ isDropdown = btn.dropdown,
670
+ textDef = t.lang[btnName] || btnName,
671
+
672
+ $btn = $('<button/>', {
673
+ type: 'button',
674
+ class: prefix + btnName + '-button ' + (btn.class || ''),
675
+ html: t.hasSvg ? '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' : '',
676
+ title: (btn.title || btn.text || textDef) + ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : ''),
677
+ tabindex: -1,
678
+ mousedown: function () {
679
+ if (!isDropdown || $('.' + btnName + '-' + prefix + 'dropdown', t.$box).is(':hidden')) {
680
+ $('body', t.doc).trigger('mousedown');
681
+ }
682
+
683
+ if (t.$btnPane.hasClass(prefix + 'disable') && !$(this).hasClass(prefix + 'active') && !$(this).hasClass(prefix + 'not-disable')) {
684
+ return false;
685
+ }
686
+
687
+ t.execCmd((isDropdown ? 'dropdown' : false) || btn.fn || btnName, btn.param || btnName, btn.forceCss || false);
688
+
689
+ return false;
690
+ }
691
+ });
692
+
693
+ if (isDropdown) {
694
+ $btn.addClass(prefix + 'open-dropdown');
695
+ var dropdownPrefix = prefix + 'dropdown',
696
+ $dropdown = $('<div/>', { // the dropdown
697
+ class: dropdownPrefix + '-' + btnName + ' ' + dropdownPrefix + ' ' + prefix + 'fixed-top',
698
+ 'data-dropdown': btnName
699
+ });
700
+ $.each(isDropdown, function (i, def) {
701
+ if (t.btnsDef[def] && t.isSupportedBtn(def)) {
702
+ $dropdown.append(t.buildSubBtn(def));
703
+ }
704
+ });
705
+ t.$box.append($dropdown.hide());
706
+ } else if (btn.key) {
707
+ t.keys[btn.key] = {
708
+ fn: btn.fn || btnName,
709
+ param: btn.param || btnName
710
+ };
711
+ }
712
+
713
+ if (!isDropdown) {
714
+ t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName;
715
+ }
716
+
717
+ return $btn;
718
+ },
719
+ // Build a button for dropdown menu
720
+ // @param n : name of the subbutton
721
+ buildSubBtn: function (btnName) {
722
+ var t = this,
723
+ prefix = t.o.prefix,
724
+ btn = t.btnsDef[btnName];
725
+
726
+ if (btn.key) {
727
+ t.keys[btn.key] = {
728
+ fn: btn.fn || btnName,
729
+ param: btn.param || btnName
730
+ };
731
+ }
732
+
733
+ t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName;
734
+
735
+ return $('<button/>', {
736
+ type: 'button',
737
+ class: prefix + btnName + '-dropdown-button' + (btn.ico ? ' ' + prefix + btn.ico + '-button' : ''),
738
+ html: t.hasSvg ? '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' + (btn.text || btn.title || t.lang[btnName] || btnName) : '',
739
+ title: ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : null),
740
+ style: btn.style || null,
741
+ mousedown: function () {
742
+ $('body', t.doc).trigger('mousedown');
743
+
744
+ t.execCmd(btn.fn || btnName, btn.param || btnName, btn.forceCss || false);
745
+
746
+ return false;
747
+ }
748
+ });
749
+ },
750
+ // Check if button is supported
751
+ isSupportedBtn: function (b) {
752
+ try {
753
+ return this.btnsDef[b].isSupported();
754
+ } catch (c) {
755
+ }
756
+ return true;
757
+ },
758
+
759
+ // Build overlay for modal box
760
+ buildOverlay: function () {
761
+ var t = this;
762
+ t.$overlay = $('<div/>', {
763
+ class: t.o.prefix + 'overlay'
764
+ }).css({
765
+ top: t.$btnPane.outerHeight(),
766
+ height: (t.$ed.outerHeight() + 1) + 'px'
767
+ }).appendTo(t.$box);
768
+ return t.$overlay;
769
+ },
770
+ showOverlay: function () {
771
+ var t = this;
772
+ $(window).trigger('scroll');
773
+ t.$overlay.fadeIn(200);
774
+ t.$box.addClass(t.o.prefix + 'box-blur');
775
+ },
776
+ hideOverlay: function () {
777
+ var t = this;
778
+ t.$overlay.fadeOut(50);
779
+ t.$box.removeClass(t.o.prefix + 'box-blur');
780
+ },
781
+
782
+ // Management of fixed button pane
783
+ fixedBtnPaneEvents: function () {
784
+ var t = this,
785
+ fixedFullWidth = t.o.fixedFullWidth,
786
+ $box = t.$box;
787
+
788
+ if (!t.o.fixedBtnPane) {
789
+ return;
790
+ }
791
+
792
+ t.isFixed = false;
793
+
794
+ $(window)
795
+ .on('scroll resize', function () {
796
+ if (!$box) {
797
+ return;
798
+ }
799
+
800
+ t.syncCode();
801
+
802
+ var scrollTop = $(window).scrollTop(),
803
+ offset = $box.offset().top + 1,
804
+ bp = t.$btnPane,
805
+ oh = bp.outerHeight() - 2;
806
+
807
+ if ((scrollTop - offset > 0) && ((scrollTop - offset - t.height) < 0)) {
808
+ if (!t.isFixed) {
809
+ t.isFixed = true;
810
+ bp.css({
811
+ position: 'fixed',
812
+ top: 0,
813
+ left: fixedFullWidth ? '0' : 'auto',
814
+ zIndex: 7
815
+ });
816
+ $([t.$ta, t.$ed]).css({marginTop: bp.height()});
817
+ }
818
+ bp.css({
819
+ width: fixedFullWidth ? '100%' : (($box.width() - 1) + 'px')
820
+ });
821
+
822
+ $('.' + t.o.prefix + 'fixed-top', $box).css({
823
+ position: fixedFullWidth ? 'fixed' : 'absolute',
824
+ top: fixedFullWidth ? oh : oh + (scrollTop - offset) + 'px',
825
+ zIndex: 15
826
+ });
827
+ } else if (t.isFixed) {
828
+ t.isFixed = false;
829
+ bp.removeAttr('style');
830
+ $([t.$ta, t.$ed]).css({marginTop: 0});
831
+ $('.' + t.o.prefix + 'fixed-top', $box).css({
832
+ position: 'absolute',
833
+ top: oh
834
+ });
835
+ }
836
+ });
837
+ },
838
+
839
+ // Disable editor
840
+ toggleDisable: function (disable) {
841
+ var t = this,
842
+ prefix = t.o.prefix;
843
+
844
+ t.disabled = disable;
845
+
846
+ if (disable) {
847
+ t.$ta.attr('disabled', true);
848
+ } else {
849
+ t.$ta.removeAttr('disabled');
850
+ }
851
+ t.$box.toggleClass(prefix + 'disabled', disable);
852
+ t.$ed.attr('contenteditable', !disable);
853
+ },
854
+
855
+ // Destroy the editor
856
+ destroy: function () {
857
+ var t = this,
858
+ prefix = t.o.prefix,
859
+ height = t.height;
860
+
861
+ if (t.isTextarea) {
862
+ t.$box.after(
863
+ t.$ta
864
+ .css({height: height})
865
+ .val(t.html())
866
+ .removeClass(prefix + 'textarea')
867
+ .show()
868
+ );
869
+ } else {
870
+ t.$box.after(
871
+ t.$ed
872
+ .css({height: height})
873
+ .removeClass(prefix + 'editor')
874
+ .removeAttr('contenteditable')
875
+ .html(t.html())
876
+ .show()
877
+ );
878
+ }
879
+
880
+ t.$ed.off('dblclick', 'img');
881
+
882
+ t.destroyPlugins();
883
+
884
+ t.$box.remove();
885
+ t.$c.removeData('trumbowyg');
886
+ $('body').removeClass(prefix + 'body-fullscreen');
887
+ },
888
+
889
+
890
+ // Empty the editor
891
+ empty: function () {
892
+ this.$ta.val('');
893
+ this.syncCode(true);
894
+ },
895
+
896
+
897
+ // Function call when click on viewHTML button
898
+ toggle: function () {
899
+ var t = this,
900
+ prefix = t.o.prefix;
901
+ t.semanticCode(false, true);
902
+ setTimeout(function () {
903
+ t.doc.activeElement.blur();
904
+ t.$box.toggleClass(prefix + 'editor-hidden ' + prefix + 'editor-visible');
905
+ t.$btnPane.toggleClass(prefix + 'disable');
906
+ $('.' + prefix + 'viewHTML-button', t.$btnPane).toggleClass(prefix + 'active');
907
+ if (t.$box.hasClass(prefix + 'editor-visible')) {
908
+ t.$ta.attr('tabindex', -1);
909
+ } else {
910
+ t.$ta.removeAttr('tabindex');
911
+ }
912
+ }, 0);
913
+ },
914
+
915
+ // Open dropdown when click on a button which open that
916
+ dropdown: function (name) {
917
+ var t = this,
918
+ d = t.doc,
919
+ prefix = t.o.prefix,
920
+ $dropdown = $('[data-dropdown=' + name + ']', t.$box),
921
+ $btn = $('.' + prefix + name + '-button', t.$btnPane),
922
+ show = $dropdown.is(':hidden');
923
+
924
+ $('body', d).trigger('mousedown');
925
+
926
+ if (show) {
927
+ var o = $btn.offset().left;
928
+ $btn.addClass(prefix + 'active');
929
+
930
+ $dropdown.css({
931
+ position: 'absolute',
932
+ top: $btn.offset().top - t.$btnPane.offset().top + $btn.outerHeight(),
933
+ left: (t.o.fixedFullWidth && t.isFixed) ? o + 'px' : (o - t.$btnPane.offset().left) + 'px'
934
+ }).show();
935
+
936
+ $(window).trigger('scroll');
937
+
938
+ $('body', d).on('mousedown', function () {
939
+ $('.' + prefix + 'dropdown', d).hide();
940
+ $('.' + prefix + 'active', d).removeClass(prefix + 'active');
941
+ $('body', d).off('mousedown');
942
+ });
943
+ }
944
+ },
945
+
946
+
947
+ // HTML Code management
948
+ html: function (html) {
949
+ var t = this;
950
+ if (html != null) {
951
+ t.$ta.val(html);
952
+ t.syncCode(true);
953
+ return t;
954
+ }
955
+ return t.$ta.val();
956
+ },
957
+ syncTextarea: function () {
958
+ var t = this;
959
+ t.$ta.val(t.$ed.text().trim().length > 0 || t.$ed.find('hr,img,embed,input').length > 0 ? t.$ed.html() : '');
960
+ },
961
+ syncCode: function (force) {
962
+ var t = this;
963
+ if (!force && t.$ed.is(':visible')) {
964
+ t.syncTextarea();
965
+ } else {
966
+ t.$ed.html(t.$ta.val());
967
+ }
968
+
969
+ if (t.o.autogrow) {
970
+ t.height = t.$ed.height();
971
+ if (t.height !== t.$ta.css('height')) {
972
+ t.$ta.css({height: t.height});
973
+ t.$c.trigger('tbwresize');
974
+ }
975
+ }
976
+ },
977
+
978
+ // Analyse and update to semantic code
979
+ // @param force : force to sync code from textarea
980
+ // @param full : wrap text nodes in <p>
981
+ semanticCode: function (force, full) {
982
+ var t = this;
983
+ t.saveRange();
984
+ t.syncCode(force);
985
+
986
+ $(t.o.tagsToRemove.join(','), t.$ed).remove();
987
+
988
+ if (t.o.semantic) {
989
+ t.semanticTag('b', 'strong');
990
+ t.semanticTag('i', 'em');
991
+ t.semanticTag('strike', 'del');
992
+
993
+ if (full) {
994
+ var inlineElementsSelector = t.o.inlineElementsSelector,
995
+ blockElementsSelector = ':not(' + inlineElementsSelector + ')';
996
+
997
+ // Wrap text nodes in span for easier processing
998
+ t.$ed.contents().filter(function () {
999
+ return this.nodeType === 3 && this.nodeValue.trim().length > 0;
1000
+ }).wrap('<span data-tbw/>');
1001
+
1002
+ // Wrap groups of inline elements in paragraphs (recursive)
1003
+ var wrapInlinesInParagraphsFrom = function ($from) {
1004
+ if ($from.length !== 0) {
1005
+ var $finalParagraph = $from.nextUntil(blockElementsSelector).andSelf().wrapAll('<p/>').parent(),
1006
+ $nextElement = $finalParagraph.nextAll(inlineElementsSelector).first();
1007
+ $finalParagraph.next('br').remove();
1008
+ wrapInlinesInParagraphsFrom($nextElement);
1009
+ }
1010
+ };
1011
+ wrapInlinesInParagraphsFrom(t.$ed.children(inlineElementsSelector).first());
1012
+
1013
+ t.semanticTag('div', 'p', true);
1014
+
1015
+ // Unwrap paragraphs content, containing nothing usefull
1016
+ t.$ed.find('p').filter(function () {
1017
+ // Don't remove currently being edited element
1018
+ if (t.range && this === t.range.startContainer) {
1019
+ return false;
1020
+ }
1021
+ return $(this).text().trim().length === 0 && $(this).children().not('br,span').length === 0;
1022
+ }).contents().unwrap();
1023
+
1024
+ // Get rid of temporial span's
1025
+ $('[data-tbw]', t.$ed).contents().unwrap();
1026
+
1027
+ // Remove empty <p>
1028
+ t.$ed.find('p:empty').remove();
1029
+ }
1030
+
1031
+ t.restoreRange();
1032
+
1033
+ t.syncTextarea();
1034
+ }
1035
+ },
1036
+
1037
+ semanticTag: function (oldTag, newTag, copyAttributes) {
1038
+ $(oldTag, this.$ed).each(function () {
1039
+ var $oldTag = $(this);
1040
+ $oldTag.wrap('<' + newTag + '/>');
1041
+ if (copyAttributes) {
1042
+ $.each($oldTag.prop('attributes'), function () {
1043
+ $oldTag.parent().attr(this.name, this.value);
1044
+ });
1045
+ }
1046
+ $oldTag.contents().unwrap();
1047
+ });
1048
+ },
1049
+
1050
+ // Function call when user click on "Insert Link"
1051
+ createLink: function () {
1052
+ var t = this,
1053
+ documentSelection = t.doc.getSelection(),
1054
+ node = documentSelection.focusNode,
1055
+ url,
1056
+ title,
1057
+ target;
1058
+
1059
+ while (['A', 'DIV'].indexOf(node.nodeName) < 0) {
1060
+ node = node.parentNode;
1061
+ }
1062
+
1063
+ if (node && node.nodeName === 'A') {
1064
+ var $a = $(node);
1065
+ url = $a.attr('href');
1066
+ title = $a.attr('title');
1067
+ target = $a.attr('target');
1068
+ var range = t.doc.createRange();
1069
+ range.selectNode(node);
1070
+ documentSelection.addRange(range);
1071
+ }
1072
+
1073
+ t.saveRange();
1074
+
1075
+ t.openModalInsert(t.lang.createLink, {
1076
+ url: {
1077
+ label: 'URL',
1078
+ required: true,
1079
+ value: url
1080
+ },
1081
+ title: {
1082
+ label: t.lang.title,
1083
+ value: title
1084
+ },
1085
+ text: {
1086
+ label: t.lang.text,
1087
+ value: t.getRangeText()
1088
+ },
1089
+ target: {
1090
+ label: t.lang.target,
1091
+ value: target
1092
+ }
1093
+ }, function (v) { // v is value
1094
+ var link = $(['<a href="', v.url, '">', v.text, '</a>'].join(''));
1095
+ if (v.title.length > 0) {
1096
+ link.attr('title', v.title);
1097
+ }
1098
+ if (v.target.length > 0) {
1099
+ link.attr('target', v.target);
1100
+ }
1101
+ t.range.deleteContents();
1102
+ t.range.insertNode(link[0]);
1103
+ return true;
1104
+ });
1105
+ },
1106
+ unlink: function () {
1107
+ var t = this,
1108
+ documentSelection = t.doc.getSelection(),
1109
+ node = documentSelection.focusNode;
1110
+
1111
+ if (documentSelection.isCollapsed) {
1112
+ while (['A', 'DIV'].indexOf(node.nodeName) < 0) {
1113
+ node = node.parentNode;
1114
+ }
1115
+
1116
+ if (node && node.nodeName === 'A') {
1117
+ var range = t.doc.createRange();
1118
+ range.selectNode(node);
1119
+ documentSelection.addRange(range);
1120
+ }
1121
+ }
1122
+ t.execCmd('unlink', undefined, undefined, true);
1123
+ },
1124
+ insertImage: function () {
1125
+ var t = this;
1126
+ t.saveRange();
1127
+ t.openModalInsert(t.lang.insertImage, {
1128
+ url: {
1129
+ label: 'URL',
1130
+ required: true
1131
+ },
1132
+ alt: {
1133
+ label: t.lang.description,
1134
+ value: t.getRangeText()
1135
+ }
1136
+ }, function (v) { // v are values
1137
+ t.execCmd('insertImage', v.url);
1138
+ $('img[src="' + v.url + '"]:not([alt])', t.$box).attr('alt', v.alt);
1139
+ return true;
1140
+ });
1141
+ },
1142
+ fullscreen: function () {
1143
+ var t = this,
1144
+ prefix = t.o.prefix,
1145
+ fullscreenCssClass = prefix + 'fullscreen',
1146
+ isFullscreen;
1147
+
1148
+ t.$box.toggleClass(fullscreenCssClass);
1149
+ isFullscreen = t.$box.hasClass(fullscreenCssClass);
1150
+ $('body').toggleClass(prefix + 'body-fullscreen', isFullscreen);
1151
+ $(window).trigger('scroll');
1152
+ t.$c.trigger('tbw' + (isFullscreen ? 'open' : 'close') + 'fullscreen');
1153
+ },
1154
+
1155
+
1156
+ /*
1157
+ * Call method of trumbowyg if exist
1158
+ * else try to call anonymous function
1159
+ * and finaly native execCommand
1160
+ */
1161
+ execCmd: function (cmd, param, forceCss, skipTrumbowyg) {
1162
+ var t = this;
1163
+ skipTrumbowyg = !!skipTrumbowyg || '';
1164
+
1165
+ if (cmd !== 'dropdown') {
1166
+ t.$ed.focus();
1167
+ }
1168
+
1169
+ t.doc.execCommand('styleWithCSS', false, forceCss || false);
1170
+
1171
+ try {
1172
+ t[cmd + skipTrumbowyg](param);
1173
+ } catch (c) {
1174
+ try {
1175
+ cmd(param);
1176
+ } catch (e2) {
1177
+ if (cmd === 'insertHorizontalRule') {
1178
+ param = undefined;
1179
+ } else if (cmd === 'formatBlock' && (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') !== -1)) {
1180
+ param = '<' + param + '>';
1181
+ }
1182
+
1183
+ t.doc.execCommand(cmd, false, param);
1184
+
1185
+ t.syncCode();
1186
+ t.semanticCode(false, true);
1187
+ }
1188
+
1189
+ if (cmd !== 'dropdown') {
1190
+ t.updateButtonPaneStatus();
1191
+ t.$c.trigger('tbwchange');
1192
+ }
1193
+ }
1194
+ },
1195
+
1196
+
1197
+ // Open a modal box
1198
+ openModal: function (title, content) {
1199
+ var t = this,
1200
+ prefix = t.o.prefix;
1201
+
1202
+ // No open a modal box when exist other modal box
1203
+ if ($('.' + prefix + 'modal-box', t.$box).length > 0) {
1204
+ return false;
1205
+ }
1206
+
1207
+ t.saveRange();
1208
+ t.showOverlay();
1209
+
1210
+ // Disable all btnPane btns
1211
+ t.$btnPane.addClass(prefix + 'disable');
1212
+
1213
+ // Build out of ModalBox, it's the mask for animations
1214
+ var $modal = $('<div/>', {
1215
+ class: prefix + 'modal ' + prefix + 'fixed-top'
1216
+ }).css({
1217
+ top: t.$btnPane.height()
1218
+ }).appendTo(t.$box);
1219
+
1220
+ // Click on overlay close modal by cancelling them
1221
+ t.$overlay.one('click', function () {
1222
+ $modal.trigger('tbwcancel');
1223
+ return false;
1224
+ });
1225
+
1226
+ // Build the form
1227
+ var $form = $('<form/>', {
1228
+ action: '',
1229
+ html: content
1230
+ })
1231
+ .on('submit', function () {
1232
+ $modal.trigger('tbwconfirm');
1233
+ return false;
1234
+ })
1235
+ .on('reset', function () {
1236
+ $modal.trigger('tbwcancel');
1237
+ return false;
1238
+ });
1239
+
1240
+
1241
+ // Build ModalBox and animate to show them
1242
+ var $box = $('<div/>', {
1243
+ class: prefix + 'modal-box',
1244
+ html: $form
1245
+ })
1246
+ .css({
1247
+ top: '-' + t.$btnPane.outerHeight() + 'px',
1248
+ opacity: 0
1249
+ })
1250
+ .appendTo($modal)
1251
+ .animate({
1252
+ top: 0,
1253
+ opacity: 1
1254
+ }, 100);
1255
+
1256
+
1257
+ // Append title
1258
+ $('<span/>', {
1259
+ text: title,
1260
+ class: prefix + 'modal-title'
1261
+ }).prependTo($box);
1262
+
1263
+ $modal.height($box.outerHeight() + 10);
1264
+
1265
+
1266
+ // Focus in modal box
1267
+ $('input:first', $box).focus();
1268
+
1269
+
1270
+ // Append Confirm and Cancel buttons
1271
+ t.buildModalBtn('submit', $box);
1272
+ t.buildModalBtn('reset', $box);
1273
+
1274
+
1275
+ $(window).trigger('scroll');
1276
+
1277
+ return $modal;
1278
+ },
1279
+ // @param n is name of modal
1280
+ buildModalBtn: function (n, $modal) {
1281
+ var t = this,
1282
+ prefix = t.o.prefix;
1283
+
1284
+ return $('<button/>', {
1285
+ class: prefix + 'modal-button ' + prefix + 'modal-' + n,
1286
+ type: n,
1287
+ text: t.lang[n] || n
1288
+ }).appendTo($('form', $modal));
1289
+ },
1290
+ // close current modal box
1291
+ closeModal: function () {
1292
+ var t = this,
1293
+ prefix = t.o.prefix;
1294
+
1295
+ t.$btnPane.removeClass(prefix + 'disable');
1296
+ t.$overlay.off();
1297
+
1298
+ // Find the modal box
1299
+ var $mb = $('.' + prefix + 'modal-box', t.$box);
1300
+
1301
+ $mb.animate({
1302
+ top: '-' + $mb.height()
1303
+ }, 100, function () {
1304
+ $mb.parent().remove();
1305
+ t.hideOverlay();
1306
+ });
1307
+
1308
+ t.restoreRange();
1309
+ },
1310
+ // Preformated build and management modal
1311
+ openModalInsert: function (title, fields, cmd) {
1312
+ var t = this,
1313
+ prefix = t.o.prefix,
1314
+ lg = t.lang,
1315
+ html = '',
1316
+ CONFIRM_EVENT = 'tbwconfirm';
1317
+
1318
+ $.each(fields, function (fieldName, field) {
1319
+ var l = field.label,
1320
+ n = field.name || fieldName;
1321
+
1322
+ html += '<label><input type="' + (field.type || 'text') + '" name="' + n + '" value="' + (field.value || '').replace(/"/g, '&quot;') + '"><span class="' + prefix + 'input-infos"><span>' +
1323
+ ((!l) ? (lg[fieldName] ? lg[fieldName] : fieldName) : (lg[l] ? lg[l] : l)) +
1324
+ '</span></span></label>';
1325
+ });
1326
+
1327
+ return t.openModal(title, html)
1328
+ .on(CONFIRM_EVENT, function () {
1329
+ var $form = $('form', $(this)),
1330
+ valid = true,
1331
+ values = {};
1332
+
1333
+ $.each(fields, function (fieldName, field) {
1334
+ var $field = $('input[name="' + fieldName + '"]', $form);
1335
+
1336
+ values[fieldName] = $.trim($field.val());
1337
+
1338
+ // Validate value
1339
+ if (field.required && values[fieldName] === '') {
1340
+ valid = false;
1341
+ t.addErrorOnModalField($field, t.lang.required);
1342
+ } else if (field.pattern && !field.pattern.test(values[fieldName])) {
1343
+ valid = false;
1344
+ t.addErrorOnModalField($field, field.patternError);
1345
+ }
1346
+ });
1347
+
1348
+ if (valid) {
1349
+ t.restoreRange();
1350
+
1351
+ if (cmd(values, fields)) {
1352
+ t.syncCode();
1353
+ t.$c.trigger('tbwchange');
1354
+ t.closeModal();
1355
+ $(this).off(CONFIRM_EVENT);
1356
+ }
1357
+ }
1358
+ })
1359
+ .one('tbwcancel', function () {
1360
+ $(this).off(CONFIRM_EVENT);
1361
+ t.closeModal();
1362
+ });
1363
+ },
1364
+ addErrorOnModalField: function ($field, err) {
1365
+ var prefix = this.o.prefix,
1366
+ $label = $field.parent();
1367
+
1368
+ $field
1369
+ .on('change keyup', function () {
1370
+ $label.removeClass(prefix + 'input-error');
1371
+ });
1372
+
1373
+ $label
1374
+ .addClass(prefix + 'input-error')
1375
+ .find('input+span')
1376
+ .append(
1377
+ $('<span/>', {
1378
+ class: prefix + 'msg-error',
1379
+ text: err
1380
+ })
1381
+ );
1382
+ },
1383
+
1384
+
1385
+ // Range management
1386
+ saveRange: function () {
1387
+ var t = this,
1388
+ documentSelection = t.doc.getSelection();
1389
+
1390
+ t.range = null;
1391
+
1392
+ if (documentSelection.rangeCount) {
1393
+ var savedRange = t.range = documentSelection.getRangeAt(0),
1394
+ range = t.doc.createRange(),
1395
+ rangeStart;
1396
+ range.selectNodeContents(t.$ed[0]);
1397
+ range.setEnd(savedRange.startContainer, savedRange.startOffset);
1398
+ rangeStart = (range + '').length;
1399
+ t.metaRange = {
1400
+ start: rangeStart,
1401
+ end: rangeStart + (savedRange + '').length
1402
+ };
1403
+ }
1404
+ },
1405
+ restoreRange: function () {
1406
+ var t = this,
1407
+ metaRange = t.metaRange,
1408
+ savedRange = t.range,
1409
+ documentSelection = t.doc.getSelection(),
1410
+ range;
1411
+
1412
+ if (!savedRange) {
1413
+ return;
1414
+ }
1415
+
1416
+ if (metaRange && metaRange.start !== metaRange.end) { // Algorithm from http://jsfiddle.net/WeWy7/3/
1417
+ var charIndex = 0,
1418
+ nodeStack = [t.$ed[0]],
1419
+ node,
1420
+ foundStart = false,
1421
+ stop = false;
1422
+
1423
+ range = t.doc.createRange();
1424
+
1425
+ while (!stop && (node = nodeStack.pop())) {
1426
+ if (node.nodeType === 3) {
1427
+ var nextCharIndex = charIndex + node.length;
1428
+ if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) {
1429
+ range.setStart(node, metaRange.start - charIndex);
1430
+ foundStart = true;
1431
+ }
1432
+ if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) {
1433
+ range.setEnd(node, metaRange.end - charIndex);
1434
+ stop = true;
1435
+ }
1436
+ charIndex = nextCharIndex;
1437
+ } else {
1438
+ var cn = node.childNodes,
1439
+ i = cn.length;
1440
+
1441
+ while (i > 0) {
1442
+ i -= 1;
1443
+ nodeStack.push(cn[i]);
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+
1449
+ documentSelection.removeAllRanges();
1450
+ documentSelection.addRange(range || savedRange);
1451
+ },
1452
+ getRangeText: function () {
1453
+ return this.range + '';
1454
+ },
1455
+
1456
+ updateButtonPaneStatus: function () {
1457
+ var t = this,
1458
+ prefix = t.o.prefix,
1459
+ tags = t.getTagsRecursive(t.doc.getSelection().focusNode.parentNode),
1460
+ activeClasses = prefix + 'active-button ' + prefix + 'active';
1461
+
1462
+ $('.' + prefix + 'active-button', t.$btnPane).removeClass(activeClasses);
1463
+ $.each(tags, function (i, tag) {
1464
+ var btnName = t.tagToButton[tag.toLowerCase()],
1465
+ $btn = $('.' + prefix + btnName + '-button', t.$btnPane);
1466
+
1467
+ if ($btn.length > 0) {
1468
+ $btn.addClass(activeClasses);
1469
+ } else {
1470
+ try {
1471
+ $btn = $('.' + prefix + 'dropdown .' + prefix + btnName + '-dropdown-button', t.$box);
1472
+ var dropdownBtnName = $btn.parent().data('dropdown');
1473
+ $('.' + prefix + dropdownBtnName + '-button', t.$box).addClass(activeClasses);
1474
+ } catch (e) {
1475
+ }
1476
+ }
1477
+ });
1478
+ },
1479
+ getTagsRecursive: function (element, tags) {
1480
+ var t = this;
1481
+ tags = tags || [];
1482
+
1483
+ var tag = element.tagName;
1484
+ if (tag === 'DIV') {
1485
+ return tags;
1486
+ }
1487
+ if (tag === 'P' && element.style.textAlign !== '') {
1488
+ tags.push(element.style.textAlign);
1489
+ }
1490
+
1491
+ $.each(t.tagHandlers, function (i, tagHandler) {
1492
+ tags = tags.concat(tagHandler(element, t));
1493
+ });
1494
+
1495
+ tags.push(tag);
1496
+
1497
+ return t.getTagsRecursive(element.parentNode, tags);
1498
+ },
1499
+
1500
+ // Plugins
1501
+ initPlugins: function () {
1502
+ var t = this;
1503
+ t.loadedPlugins = [];
1504
+ $.each($.trumbowyg.plugins, function (name, plugin) {
1505
+ if (!plugin.shouldInit || plugin.shouldInit(t)) {
1506
+ plugin.init(t);
1507
+ if (plugin.tagHandler) {
1508
+ t.tagHandlers.push(plugin.tagHandler);
1509
+ }
1510
+ t.loadedPlugins.push(plugin);
1511
+ }
1512
+ });
1513
+ },
1514
+ destroyPlugins: function () {
1515
+ $.each(this.loadedPlugins, function (i, plugin) {
1516
+ if (plugin.destroy) {
1517
+ plugin.destroy();
1518
+ }
1519
+ });
1520
+ }
1521
+ };
1522
+ })(navigator, window, document, jQuery);