activeadmin_trumbowyg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +60 -0
  6. data/Rakefile +3 -0
  7. data/activeadmin_trumbowyg.gemspec +19 -0
  8. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ar.min.js +11 -0
  9. data/app/assets/javascripts/activeadmin/trumbowyg/langs/bg.min.js +8 -0
  10. data/app/assets/javascripts/activeadmin/trumbowyg/langs/by.min.js +8 -0
  11. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ca.min.js +11 -0
  12. data/app/assets/javascripts/activeadmin/trumbowyg/langs/cs.min.js +8 -0
  13. data/app/assets/javascripts/activeadmin/trumbowyg/langs/da.min.js +9 -0
  14. data/app/assets/javascripts/activeadmin/trumbowyg/langs/de.min.js +9 -0
  15. data/app/assets/javascripts/activeadmin/trumbowyg/langs/el.min.js +12 -0
  16. data/app/assets/javascripts/activeadmin/trumbowyg/langs/es.min.js +9 -0
  17. data/app/assets/javascripts/activeadmin/trumbowyg/langs/es_ar.min.js +10 -0
  18. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fa.min.js +10 -0
  19. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fi.min.js +9 -0
  20. data/app/assets/javascripts/activeadmin/trumbowyg/langs/fr.min.js +10 -0
  21. data/app/assets/javascripts/activeadmin/trumbowyg/langs/he.min.js +9 -0
  22. data/app/assets/javascripts/activeadmin/trumbowyg/langs/hr.min.js +9 -0
  23. data/app/assets/javascripts/activeadmin/trumbowyg/langs/hu.min.js +10 -0
  24. data/app/assets/javascripts/activeadmin/trumbowyg/langs/id.min.js +11 -0
  25. data/app/assets/javascripts/activeadmin/trumbowyg/langs/it.min.js +8 -0
  26. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ja.min.js +10 -0
  27. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ko.min.js +10 -0
  28. data/app/assets/javascripts/activeadmin/trumbowyg/langs/mn.min.js +8 -0
  29. data/app/assets/javascripts/activeadmin/trumbowyg/langs/my.min.js +8 -0
  30. data/app/assets/javascripts/activeadmin/trumbowyg/langs/nl.min.js +9 -0
  31. data/app/assets/javascripts/activeadmin/trumbowyg/langs/no_nb.min.js +10 -0
  32. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ph.min.js +8 -0
  33. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pl.min.js +9 -0
  34. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pt.min.js +11 -0
  35. data/app/assets/javascripts/activeadmin/trumbowyg/langs/pt_br.min.js +11 -0
  36. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ro.min.js +12 -0
  37. data/app/assets/javascripts/activeadmin/trumbowyg/langs/rs.min.js +8 -0
  38. data/app/assets/javascripts/activeadmin/trumbowyg/langs/rs_latin.min.js +9 -0
  39. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ru.min.js +8 -0
  40. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sk.min.js +8 -0
  41. data/app/assets/javascripts/activeadmin/trumbowyg/langs/sv.min.js +12 -0
  42. data/app/assets/javascripts/activeadmin/trumbowyg/langs/tr.min.js +9 -0
  43. data/app/assets/javascripts/activeadmin/trumbowyg/langs/ua.min.js +8 -0
  44. data/app/assets/javascripts/activeadmin/trumbowyg/langs/vi.min.js +9 -0
  45. data/app/assets/javascripts/activeadmin/trumbowyg/langs/zh_cn.min.js +11 -0
  46. data/app/assets/javascripts/activeadmin/trumbowyg/langs/zh_tw.min.js +11 -0
  47. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.js +121 -0
  48. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.min.js +1 -0
  49. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.js +179 -0
  50. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.min.js +1 -0
  51. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.js +176 -0
  52. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.min.js +1 -0
  53. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/sass/trumbowyg.colors.scss +49 -0
  54. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.css +43 -0
  55. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.min.css +2 -0
  56. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.js +965 -0
  57. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.min.js +1 -0
  58. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/sass/trumbowyg.emoji.scss +56 -0
  59. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.css +47 -0
  60. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.min.css +2 -0
  61. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.js +83 -0
  62. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.min.js +1 -0
  63. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.js +101 -0
  64. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.min.js +1 -0
  65. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/pasteimage/trumbowyg.pasteimage.js +41 -0
  66. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/pasteimage/trumbowyg.pasteimage.min.js +1 -0
  67. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.js +123 -0
  68. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.min.js +1 -0
  69. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/trumbowyg.table.js +154 -0
  70. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/table/trumbowyg.table.min.js +1 -0
  71. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/template/trumbowyg.template.js +58 -0
  72. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/template/trumbowyg.template.min.js +1 -0
  73. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.js +242 -0
  74. data/app/assets/javascripts/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.min.js +1 -0
  75. data/app/assets/javascripts/activeadmin/trumbowyg/trumbowyg.js +1688 -0
  76. data/app/assets/javascripts/activeadmin/trumbowyg/trumbowyg.min.js +2 -0
  77. data/app/assets/javascripts/activeadmin/trumbowyg/ui/icons.svg +1 -0
  78. data/app/assets/javascripts/activeadmin/trumbowyg/ui/sass/trumbowyg.scss +796 -0
  79. data/app/assets/javascripts/activeadmin/trumbowyg/ui/trumbowyg.css +584 -0
  80. data/app/assets/javascripts/activeadmin/trumbowyg/ui/trumbowyg.min.css +2 -0
  81. data/app/assets/javascripts/activeadmin/trumbowyg_input.js +7 -0
  82. data/app/assets/stylesheets/activeadmin/_trumbowyg_input.sass +48 -0
  83. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ar.min.js +11 -0
  84. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/bg.min.js +8 -0
  85. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/by.min.js +8 -0
  86. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ca.min.js +11 -0
  87. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/cs.min.js +8 -0
  88. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/da.min.js +9 -0
  89. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/de.min.js +9 -0
  90. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/el.min.js +12 -0
  91. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/es.min.js +9 -0
  92. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/es_ar.min.js +10 -0
  93. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/fa.min.js +10 -0
  94. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/fi.min.js +9 -0
  95. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/fr.min.js +10 -0
  96. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/he.min.js +9 -0
  97. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/hr.min.js +9 -0
  98. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/hu.min.js +10 -0
  99. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/id.min.js +11 -0
  100. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/it.min.js +8 -0
  101. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ja.min.js +10 -0
  102. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ko.min.js +10 -0
  103. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/mn.min.js +8 -0
  104. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/my.min.js +8 -0
  105. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/nl.min.js +9 -0
  106. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/no_nb.min.js +10 -0
  107. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ph.min.js +8 -0
  108. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/pl.min.js +9 -0
  109. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/pt.min.js +11 -0
  110. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/pt_br.min.js +11 -0
  111. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ro.min.js +12 -0
  112. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/rs.min.js +8 -0
  113. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/rs_latin.min.js +9 -0
  114. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ru.min.js +8 -0
  115. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/sk.min.js +8 -0
  116. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/sv.min.js +12 -0
  117. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/tr.min.js +9 -0
  118. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/ua.min.js +8 -0
  119. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/vi.min.js +9 -0
  120. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/zh_cn.min.js +11 -0
  121. data/app/assets/stylesheets/activeadmin/trumbowyg/langs/zh_tw.min.js +11 -0
  122. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.js +121 -0
  123. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/base64/trumbowyg.base64.min.js +1 -0
  124. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.js +179 -0
  125. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/cleanpaste/trumbowyg.cleanpaste.min.js +1 -0
  126. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.js +176 -0
  127. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/colors/trumbowyg.colors.min.js +1 -0
  128. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/colors/ui/sass/trumbowyg.colors.scss +49 -0
  129. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.css +43 -0
  130. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/colors/ui/trumbowyg.colors.min.css +2 -0
  131. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.js +965 -0
  132. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/emoji/trumbowyg.emoji.min.js +1 -0
  133. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/emoji/ui/sass/trumbowyg.emoji.scss +56 -0
  134. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.css +47 -0
  135. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/emoji/ui/trumbowyg.emoji.min.css +2 -0
  136. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.js +83 -0
  137. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/insertaudio/trumbowyg.insertaudio.min.js +1 -0
  138. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.js +101 -0
  139. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/noembed/trumbowyg.noembed.min.js +1 -0
  140. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/pasteimage/trumbowyg.pasteimage.js +41 -0
  141. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/pasteimage/trumbowyg.pasteimage.min.js +1 -0
  142. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.js +123 -0
  143. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/preformatted/trumbowyg.preformatted.min.js +1 -0
  144. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/table/trumbowyg.table.js +154 -0
  145. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/table/trumbowyg.table.min.js +1 -0
  146. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/template/trumbowyg.template.js +58 -0
  147. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/template/trumbowyg.template.min.js +1 -0
  148. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.js +242 -0
  149. data/app/assets/stylesheets/activeadmin/trumbowyg/plugins/upload/trumbowyg.upload.min.js +1 -0
  150. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.js +1688 -0
  151. data/app/assets/stylesheets/activeadmin/trumbowyg/trumbowyg.min.js +2 -0
  152. data/app/assets/stylesheets/activeadmin/trumbowyg/ui/icons.svg +1 -0
  153. data/app/assets/stylesheets/activeadmin/trumbowyg/ui/sass/trumbowyg.scss +796 -0
  154. data/app/assets/stylesheets/activeadmin/trumbowyg/ui/trumbowyg.css +584 -0
  155. data/app/assets/stylesheets/activeadmin/trumbowyg/ui/trumbowyg.min.css +2 -0
  156. data/lib/activeadmin/trumbowyg/engine.rb +9 -0
  157. data/lib/activeadmin/trumbowyg/version.rb +5 -0
  158. data/lib/activeadmin/trumbowyg.rb +1 -0
  159. data/lib/activeadmin_trumbowyg.rb +3 -0
  160. data/lib/formtastic/inputs/trumbowyg_input.rb +12 -0
  161. data/screenshot.jpg +0 -0
  162. metadata +217 -0
@@ -0,0 +1,1688 @@
1
+ /**
2
+ * Trumbowyg v2.8.1 - A lightweight WYSIWYG editor
3
+ * Trumbowyg core file
4
+ * ------------------------
5
+ * @link http://alex-d.github.io/Trumbowyg
6
+ * @license MIT
7
+ * @author Alexandre Demode (Alex-D)
8
+ * Twitter : @AlexandreDemode
9
+ * Website : alex-d.fr
10
+ */
11
+
12
+ jQuery.trumbowyg = {
13
+ langs: {
14
+ en: {
15
+ viewHTML: 'View HTML',
16
+
17
+ undo: 'Undo',
18
+ redo: 'Redo',
19
+
20
+ formatting: 'Formatting',
21
+ p: 'Paragraph',
22
+ blockquote: 'Quote',
23
+ code: 'Code',
24
+ header: 'Header',
25
+
26
+ bold: 'Bold',
27
+ italic: 'Italic',
28
+ strikethrough: 'Stroke',
29
+ underline: 'Underline',
30
+
31
+ strong: 'Strong',
32
+ em: 'Emphasis',
33
+ del: 'Deleted',
34
+
35
+ superscript: 'Superscript',
36
+ subscript: 'Subscript',
37
+
38
+ unorderedList: 'Unordered list',
39
+ orderedList: 'Ordered list',
40
+
41
+ insertImage: 'Insert Image',
42
+ link: 'Link',
43
+ createLink: 'Insert link',
44
+ unlink: 'Remove link',
45
+
46
+ justifyLeft: 'Align Left',
47
+ justifyCenter: 'Align Center',
48
+ justifyRight: 'Align Right',
49
+ justifyFull: 'Align Justify',
50
+
51
+ horizontalRule: 'Insert horizontal rule',
52
+ removeformat: 'Remove format',
53
+
54
+ fullscreen: 'Fullscreen',
55
+
56
+ close: 'Close',
57
+
58
+ submit: 'Confirm',
59
+ reset: 'Cancel',
60
+
61
+ required: 'Required',
62
+ description: 'Description',
63
+ title: 'Title',
64
+ text: 'Text',
65
+ target: 'Target'
66
+ }
67
+ },
68
+
69
+ // Plugins
70
+ plugins: {},
71
+
72
+ // SVG Path globally
73
+ svgPath: null,
74
+
75
+ hideButtonTexts: null
76
+ };
77
+
78
+ // Makes default options read-only
79
+ Object.defineProperty(jQuery.trumbowyg, 'defaultOptions', {
80
+ value: {
81
+ lang: 'en',
82
+
83
+ fixedBtnPane: false,
84
+ fixedFullWidth: false,
85
+ autogrow: false,
86
+ autogrowOnEnter: false,
87
+
88
+ prefix: 'trumbowyg-',
89
+
90
+ semantic: true,
91
+ resetCss: false,
92
+ removeformatPasted: false,
93
+ tagsToRemove: [],
94
+ btns: [
95
+ ['viewHTML'],
96
+ ['undo', 'redo'], // Only supported in Blink browsers
97
+ ['formatting'],
98
+ ['strong', 'em', 'del'],
99
+ ['superscript', 'subscript'],
100
+ ['link'],
101
+ ['insertImage'],
102
+ ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
103
+ ['unorderedList', 'orderedList'],
104
+ ['horizontalRule'],
105
+ ['removeformat'],
106
+ ['fullscreen']
107
+ ],
108
+ // For custom button definitions
109
+ btnsDef: {},
110
+
111
+ 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',
112
+
113
+ pasteHandlers: [],
114
+
115
+ // imgDblClickHandler: default is defined in constructor
116
+
117
+ plugins: {}
118
+ },
119
+ writable: false,
120
+ enumerable: true,
121
+ configurable: false
122
+ });
123
+
124
+
125
+ (function (navigator, window, document, $) {
126
+ 'use strict';
127
+
128
+ var CONFIRM_EVENT = 'tbwconfirm',
129
+ CANCEL_EVENT = 'tbwcancel';
130
+
131
+ $.fn.trumbowyg = function (options, params) {
132
+ var trumbowygDataName = 'trumbowyg';
133
+ if (options === Object(options) || !options) {
134
+ return this.each(function () {
135
+ if (!$(this).data(trumbowygDataName)) {
136
+ $(this).data(trumbowygDataName, new Trumbowyg(this, options));
137
+ }
138
+ });
139
+ }
140
+ if (this.length === 1) {
141
+ try {
142
+ var t = $(this).data(trumbowygDataName);
143
+ switch (options) {
144
+ // Exec command
145
+ case 'execCmd':
146
+ return t.execCmd(params.cmd, params.param, params.forceCss);
147
+
148
+ // Modal box
149
+ case 'openModal':
150
+ return t.openModal(params.title, params.content);
151
+ case 'closeModal':
152
+ return t.closeModal();
153
+ case 'openModalInsert':
154
+ return t.openModalInsert(params.title, params.fields, params.callback);
155
+
156
+ // Range
157
+ case 'saveRange':
158
+ return t.saveRange();
159
+ case 'getRange':
160
+ return t.range;
161
+ case 'getRangeText':
162
+ return t.getRangeText();
163
+ case 'restoreRange':
164
+ return t.restoreRange();
165
+
166
+ // Enable/disable
167
+ case 'enable':
168
+ return t.setDisabled(false);
169
+ case 'disable':
170
+ return t.setDisabled(true);
171
+
172
+ // Destroy
173
+ case 'destroy':
174
+ return t.destroy();
175
+
176
+ // Empty
177
+ case 'empty':
178
+ return t.empty();
179
+
180
+ // HTML
181
+ case 'html':
182
+ return t.html(params);
183
+ }
184
+ } catch (c) {
185
+ }
186
+ }
187
+
188
+ return false;
189
+ };
190
+
191
+ // @param: editorElem is the DOM element
192
+ var Trumbowyg = function (editorElem, options) {
193
+ var t = this,
194
+ trumbowygIconsId = 'trumbowyg-icons',
195
+ $trumbowyg = $.trumbowyg;
196
+
197
+ // Get the document of the element. It use to makes the plugin
198
+ // compatible on iframes.
199
+ t.doc = editorElem.ownerDocument || document;
200
+
201
+ // jQuery object of the editor
202
+ t.$ta = $(editorElem); // $ta : Textarea
203
+ t.$c = $(editorElem); // $c : creator
204
+
205
+ options = options || {};
206
+
207
+ // Localization management
208
+ if (options.lang != null || $trumbowyg.langs[options.lang] != null) {
209
+ t.lang = $.extend(true, {}, $trumbowyg.langs.en, $trumbowyg.langs[options.lang]);
210
+ } else {
211
+ t.lang = $trumbowyg.langs.en;
212
+ }
213
+
214
+ t.hideButtonTexts = $trumbowyg.hideButtonTexts != null ? $trumbowyg.hideButtonTexts : options.hideButtonTexts;
215
+
216
+ // SVG path
217
+ var svgPathOption = $trumbowyg.svgPath != null ? $trumbowyg.svgPath : options.svgPath;
218
+ t.hasSvg = svgPathOption !== false;
219
+ t.svgPath = !!t.doc.querySelector('base') ? window.location.href.split('#')[0] : '';
220
+ if ($('#' + trumbowygIconsId, t.doc).length === 0 && svgPathOption !== false) {
221
+ if (svgPathOption == null) {
222
+ // Hack to get svgPathOption based on trumbowyg.js path
223
+ try {
224
+ throw new Error();
225
+ } catch (e) {
226
+ if (!e.hasOwnProperty('stack')) {
227
+ console.warn('You must define svgPath: https://goo.gl/CfTY9U'); // jshint ignore:line
228
+ } else {
229
+ var stackLines = e.stack.split('\n');
230
+
231
+ for (var i in stackLines) {
232
+ if (!stackLines[i].match(/https?:\/\//)) {
233
+ continue;
234
+ }
235
+ svgPathOption = stackLines[Number(i)].match(/((https?:\/\/.+\/)([^\/]+\.js))(\?.*)?:/)[1].split('/');
236
+ svgPathOption.pop();
237
+ svgPathOption = svgPathOption.join('/') + '/ui/icons.svg';
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ var div = t.doc.createElement('div');
245
+ div.id = trumbowygIconsId;
246
+ t.doc.body.insertBefore(div, t.doc.body.childNodes[0]);
247
+ $.ajax({
248
+ async: true,
249
+ type: 'GET',
250
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
251
+ dataType: 'xml',
252
+ crossDomain: true,
253
+ url: svgPathOption,
254
+ data: null,
255
+ beforeSend: null,
256
+ complete: null,
257
+ success: function (data) {
258
+ div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
259
+ }
260
+ });
261
+ }
262
+
263
+
264
+ /**
265
+ * When the button is associated to a empty object
266
+ * fn and title attributs are defined from the button key value
267
+ *
268
+ * For example
269
+ * foo: {}
270
+ * is equivalent to :
271
+ * foo: {
272
+ * fn: 'foo',
273
+ * title: this.lang.foo
274
+ * }
275
+ */
276
+ var h = t.lang.header, // Header translation
277
+ isBlinkFunction = function () {
278
+ return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window;
279
+ };
280
+ t.btnsDef = {
281
+ viewHTML: {
282
+ fn: 'toggle'
283
+ },
284
+
285
+ undo: {
286
+ isSupported: isBlinkFunction,
287
+ key: 'Z'
288
+ },
289
+ redo: {
290
+ isSupported: isBlinkFunction,
291
+ key: 'Y'
292
+ },
293
+
294
+ p: {
295
+ fn: 'formatBlock'
296
+ },
297
+ blockquote: {
298
+ fn: 'formatBlock'
299
+ },
300
+ h1: {
301
+ fn: 'formatBlock',
302
+ title: h + ' 1'
303
+ },
304
+ h2: {
305
+ fn: 'formatBlock',
306
+ title: h + ' 2'
307
+ },
308
+ h3: {
309
+ fn: 'formatBlock',
310
+ title: h + ' 3'
311
+ },
312
+ h4: {
313
+ fn: 'formatBlock',
314
+ title: h + ' 4'
315
+ },
316
+ subscript: {
317
+ tag: 'sub'
318
+ },
319
+ superscript: {
320
+ tag: 'sup'
321
+ },
322
+
323
+ bold: {
324
+ key: 'B',
325
+ tag: 'b'
326
+ },
327
+ italic: {
328
+ key: 'I',
329
+ tag: 'i'
330
+ },
331
+ underline: {
332
+ tag: 'u'
333
+ },
334
+ strikethrough: {
335
+ tag: 'strike'
336
+ },
337
+
338
+ strong: {
339
+ fn: 'bold',
340
+ key: 'B'
341
+ },
342
+ em: {
343
+ fn: 'italic',
344
+ key: 'I'
345
+ },
346
+ del: {
347
+ fn: 'strikethrough'
348
+ },
349
+
350
+ createLink: {
351
+ key: 'K',
352
+ tag: 'a'
353
+ },
354
+ unlink: {},
355
+
356
+ insertImage: {},
357
+
358
+ justifyLeft: {
359
+ tag: 'left',
360
+ forceCss: true
361
+ },
362
+ justifyCenter: {
363
+ tag: 'center',
364
+ forceCss: true
365
+ },
366
+ justifyRight: {
367
+ tag: 'right',
368
+ forceCss: true
369
+ },
370
+ justifyFull: {
371
+ tag: 'justify',
372
+ forceCss: true
373
+ },
374
+
375
+ unorderedList: {
376
+ fn: 'insertUnorderedList',
377
+ tag: 'ul'
378
+ },
379
+ orderedList: {
380
+ fn: 'insertOrderedList',
381
+ tag: 'ol'
382
+ },
383
+
384
+ horizontalRule: {
385
+ fn: 'insertHorizontalRule'
386
+ },
387
+
388
+ removeformat: {},
389
+
390
+ fullscreen: {
391
+ class: 'trumbowyg-not-disable'
392
+ },
393
+ close: {
394
+ fn: 'destroy',
395
+ class: 'trumbowyg-not-disable'
396
+ },
397
+
398
+ // Dropdowns
399
+ formatting: {
400
+ dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'],
401
+ ico: 'p'
402
+ },
403
+ link: {
404
+ dropdown: ['createLink', 'unlink']
405
+ }
406
+ };
407
+
408
+ // Defaults Options
409
+ t.o = $.extend(true, {}, $trumbowyg.defaultOptions, options);
410
+ if (!t.o.hasOwnProperty('imgDblClickHandler')) {
411
+ t.o.imgDblClickHandler = t.getDefaultImgDblClickHandler();
412
+ }
413
+
414
+ t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled);
415
+
416
+ if (options.btns) {
417
+ t.o.btns = options.btns;
418
+ } else if (!t.o.semantic) {
419
+ t.o.btns[3] = ['bold', 'italic', 'underline', 'strikethrough'];
420
+ }
421
+
422
+ $.each(t.o.btnsDef, function (btnName, btnDef) {
423
+ t.addBtnDef(btnName, btnDef);
424
+ });
425
+
426
+ // put this here in the event it would be merged in with options
427
+ t.eventNamespace = 'trumbowyg-event';
428
+
429
+ // Keyboard shortcuts are load in this array
430
+ t.keys = [];
431
+
432
+ // Tag to button dynamically hydrated
433
+ t.tagToButton = {};
434
+ t.tagHandlers = [];
435
+
436
+ // Admit multiple paste handlers
437
+ t.pasteHandlers = [].concat(t.o.pasteHandlers);
438
+
439
+ // Check if browser is IE
440
+ t.isIE = (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') !== -1);
441
+
442
+ t.init();
443
+ };
444
+
445
+ Trumbowyg.prototype = {
446
+ init: function () {
447
+ var t = this;
448
+ t.height = t.$ta.height();
449
+
450
+ t.initPlugins();
451
+
452
+ try {
453
+ // Disable image resize, try-catch for old IE
454
+ t.doc.execCommand('enableObjectResizing', false, false);
455
+ t.doc.execCommand('defaultParagraphSeparator', false, 'p');
456
+ } catch (e) {
457
+ }
458
+
459
+ t.buildEditor();
460
+ t.buildBtnPane();
461
+
462
+ t.fixedBtnPaneEvents();
463
+
464
+ t.buildOverlay();
465
+
466
+ setTimeout(function () {
467
+ if (t.disabled) {
468
+ t.setDisabled(true);
469
+ }
470
+ t.$c.trigger('tbwinit');
471
+ });
472
+ },
473
+
474
+ addBtnDef: function (btnName, btnDef) {
475
+ this.btnsDef[btnName] = btnDef;
476
+ },
477
+
478
+ buildEditor: function () {
479
+ var t = this,
480
+ prefix = t.o.prefix,
481
+ html = '';
482
+
483
+ t.$box = $('<div/>', {
484
+ class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg'
485
+ });
486
+
487
+ // $ta = Textarea
488
+ // $ed = Editor
489
+ t.isTextarea = t.$ta.is('textarea');
490
+ if (t.isTextarea) {
491
+ html = t.$ta.val();
492
+ t.$ed = $('<div/>');
493
+ t.$box
494
+ .insertAfter(t.$ta)
495
+ .append(t.$ed, t.$ta);
496
+ } else {
497
+ t.$ed = t.$ta;
498
+ html = t.$ed.html();
499
+
500
+ t.$ta = $('<textarea/>', {
501
+ name: t.$ta.attr('id'),
502
+ height: t.height
503
+ }).val(html);
504
+
505
+ t.$box
506
+ .insertAfter(t.$ed)
507
+ .append(t.$ta, t.$ed);
508
+ t.syncCode();
509
+ }
510
+
511
+ t.$ta
512
+ .addClass(prefix + 'textarea')
513
+ .attr('tabindex', -1)
514
+ ;
515
+
516
+ t.$ed
517
+ .addClass(prefix + 'editor')
518
+ .attr({
519
+ contenteditable: true,
520
+ dir: t.lang._dir || 'ltr'
521
+ })
522
+ .html(html)
523
+ ;
524
+
525
+ if (t.o.tabindex) {
526
+ t.$ed.attr('tabindex', t.o.tabindex);
527
+ }
528
+
529
+ if (t.$c.is('[placeholder]')) {
530
+ t.$ed.attr('placeholder', t.$c.attr('placeholder'));
531
+ }
532
+
533
+ if (t.$c.is('[spellcheck]')) {
534
+ t.$ed.attr('spellcheck', t.$c.attr('spellcheck'));
535
+ }
536
+
537
+ if (t.o.resetCss) {
538
+ t.$ed.addClass(prefix + 'reset-css');
539
+ }
540
+
541
+ if (!t.o.autogrow) {
542
+ t.$ta.add(t.$ed).css({
543
+ height: t.height
544
+ });
545
+ }
546
+
547
+ t.semanticCode();
548
+
549
+ if (t.o.autogrowOnEnter) {
550
+ t.$ed.addClass(prefix + 'autogrow-on-enter');
551
+ }
552
+
553
+ var ctrl = false,
554
+ composition = false,
555
+ debounceButtonPaneStatus,
556
+ updateEventName = t.isIE ? 'keyup' : 'input';
557
+
558
+ t.$ed
559
+ .on('dblclick', 'img', t.o.imgDblClickHandler)
560
+ .on('keydown', function (e) {
561
+ if ((e.ctrlKey || e.metaKey) && !e.altKey) {
562
+ ctrl = true;
563
+ var key = t.keys[String.fromCharCode(e.which).toUpperCase()];
564
+
565
+ try {
566
+ t.execCmd(key.fn, key.param);
567
+ return false;
568
+ } catch (c) {
569
+ }
570
+ }
571
+ })
572
+ .on('compositionstart compositionupdate', function () {
573
+ composition = true;
574
+ })
575
+ .on(updateEventName + ' compositionend', function (e) {
576
+ if (e.type === 'compositionend') {
577
+ composition = false;
578
+ } else if (composition) {
579
+ return;
580
+ }
581
+
582
+ var keyCode = e.which;
583
+
584
+ if (keyCode >= 37 && keyCode <= 40) {
585
+ return;
586
+ }
587
+
588
+ if ((e.ctrlKey || e.metaKey) && (keyCode === 89 || keyCode === 90)) {
589
+ t.$c.trigger('tbwchange');
590
+ } else if (!ctrl && keyCode !== 17) {
591
+ t.semanticCode(false, e.type === 'compositionend' && keyCode === 13);
592
+ t.$c.trigger('tbwchange');
593
+ } else if (typeof e.which === 'undefined') {
594
+ t.semanticCode(false, false, true);
595
+ }
596
+
597
+ setTimeout(function () {
598
+ ctrl = false;
599
+ }, 200);
600
+ })
601
+ .on('mouseup keydown keyup', function () {
602
+ clearTimeout(debounceButtonPaneStatus);
603
+ debounceButtonPaneStatus = setTimeout(function () {
604
+ t.updateButtonPaneStatus();
605
+ }, 50);
606
+ })
607
+ .on('focus blur', function (e) {
608
+ t.$c.trigger('tbw' + e.type);
609
+ if (e.type === 'blur') {
610
+ $('.' + prefix + 'active-button', t.$btnPane).removeClass(prefix + 'active-button ' + prefix + 'active');
611
+ }
612
+ if (t.o.autogrowOnEnter) {
613
+ if (t.autogrowOnEnterDontClose) {
614
+ return;
615
+ }
616
+ if (e.type === 'focus') {
617
+ t.autogrowOnEnterWasFocused = true;
618
+ t.autogrowEditorOnEnter();
619
+ }
620
+ else if (!t.o.autogrow) {
621
+ t.$ed.css({height: t.$ed.css('min-height')});
622
+ t.$c.trigger('tbwresize');
623
+ }
624
+ }
625
+ })
626
+ .on('cut', function () {
627
+ setTimeout(function () {
628
+ t.semanticCode(false, true);
629
+ t.$c.trigger('tbwchange');
630
+ }, 0);
631
+ })
632
+ .on('paste', function (e) {
633
+ if (t.o.removeformatPasted) {
634
+ e.preventDefault();
635
+
636
+ if (window.getSelection && window.getSelection().deleteFromDocument) {
637
+ window.getSelection().deleteFromDocument();
638
+ }
639
+
640
+ try {
641
+ // IE
642
+ var text = window.clipboardData.getData('Text');
643
+
644
+ try {
645
+ // <= IE10
646
+ t.doc.selection.createRange().pasteHTML(text);
647
+ } catch (c) {
648
+ // IE 11
649
+ t.doc.getSelection().getRangeAt(0).insertNode(t.doc.createTextNode(text));
650
+ }
651
+ t.$c.trigger('tbwchange', e);
652
+ } catch (d) {
653
+ // Not IE
654
+ t.execCmd('insertText', (e.originalEvent || e).clipboardData.getData('text/plain'));
655
+ }
656
+ }
657
+
658
+ // Call pasteHandlers
659
+ $.each(t.pasteHandlers, function (i, pasteHandler) {
660
+ pasteHandler(e);
661
+ });
662
+
663
+ setTimeout(function () {
664
+ t.semanticCode(false, true);
665
+ t.$c.trigger('tbwpaste', e);
666
+ }, 0);
667
+ });
668
+
669
+ t.$ta
670
+ .on('keyup', function () {
671
+ t.$c.trigger('tbwchange');
672
+ })
673
+ .on('paste', function () {
674
+ setTimeout(function () {
675
+ t.$c.trigger('tbwchange');
676
+ }, 0);
677
+ });
678
+
679
+ t.$box.on('keydown', function (e) {
680
+ if (e.which === 27 && $('.' + prefix + 'modal-box', t.$box).length === 1) {
681
+ t.closeModal();
682
+ return false;
683
+ }
684
+ });
685
+ },
686
+
687
+ //autogrow when entering logic
688
+ autogrowEditorOnEnter: function () {
689
+ var t = this;
690
+ t.$ed.removeClass('autogrow-on-enter');
691
+ var oldHeight = t.$ed[0].clientHeight;
692
+ t.$ed.height('auto');
693
+ var totalHeight = t.$ed[0].scrollHeight;
694
+ t.$ed.addClass('autogrow-on-enter');
695
+ if (oldHeight !== totalHeight) {
696
+ t.$ed.height(oldHeight);
697
+ setTimeout(function () {
698
+ t.$ed.css({height: totalHeight});
699
+ t.$c.trigger('tbwresize');
700
+ }, 0);
701
+ }
702
+ },
703
+
704
+
705
+ // Build button pane, use o.btns option
706
+ buildBtnPane: function () {
707
+ var t = this,
708
+ prefix = t.o.prefix;
709
+
710
+ var $btnPane = t.$btnPane = $('<div/>', {
711
+ class: prefix + 'button-pane'
712
+ });
713
+
714
+ $.each(t.o.btns, function (i, btnGrp) {
715
+ if (!$.isArray(btnGrp)) {
716
+ btnGrp = [btnGrp];
717
+ }
718
+
719
+ var $btnGroup = $('<div/>', {
720
+ class: prefix + 'button-group ' + ((btnGrp.indexOf('fullscreen') >= 0) ? prefix + 'right' : '')
721
+ });
722
+ $.each(btnGrp, function (i, btn) {
723
+ try { // Prevent buildBtn error
724
+ if (t.isSupportedBtn(btn)) { // It's a supported button
725
+ $btnGroup.append(t.buildBtn(btn));
726
+ }
727
+ } catch (c) {
728
+ }
729
+ });
730
+ $btnPane.append($btnGroup);
731
+ });
732
+
733
+ t.$box.prepend($btnPane);
734
+ },
735
+
736
+
737
+ // Build a button and his action
738
+ buildBtn: function (btnName) { // btnName is name of the button
739
+ var t = this,
740
+ prefix = t.o.prefix,
741
+ btn = t.btnsDef[btnName],
742
+ isDropdown = btn.dropdown,
743
+ hasIcon = btn.hasIcon != null ? btn.hasIcon : true,
744
+ textDef = t.lang[btnName] || btnName,
745
+
746
+ $btn = $('<button/>', {
747
+ type: 'button',
748
+ class: prefix + btnName + '-button ' + (btn.class || '') + (!hasIcon ? ' ' + prefix + 'textual-button' : ''),
749
+ html: t.hasSvg && hasIcon ?
750
+ '<svg><use xlink:href="' + t.svgPath + '#' + prefix + (btn.ico || btnName).replace(/([A-Z]+)/g, '-$1').toLowerCase() + '"/></svg>' :
751
+ t.hideButtonTexts ? '' : (btn.text || btn.title || t.lang[btnName] || btnName),
752
+ title: (btn.title || btn.text || textDef) + ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : ''),
753
+ tabindex: -1,
754
+ mousedown: function () {
755
+ if (!isDropdown || $('.' + btnName + '-' + prefix + 'dropdown', t.$box).is(':hidden')) {
756
+ $('body', t.doc).trigger('mousedown');
757
+ }
758
+
759
+ if (t.$btnPane.hasClass(prefix + 'disable') && !$(this).hasClass(prefix + 'active') && !$(this).hasClass(prefix + 'not-disable')) {
760
+ return false;
761
+ }
762
+
763
+ t.execCmd((isDropdown ? 'dropdown' : false) || btn.fn || btnName, btn.param || btnName, btn.forceCss);
764
+
765
+ return false;
766
+ }
767
+ });
768
+
769
+ if (isDropdown) {
770
+ $btn.addClass(prefix + 'open-dropdown');
771
+ var dropdownPrefix = prefix + 'dropdown',
772
+ $dropdown = $('<div/>', { // the dropdown
773
+ class: dropdownPrefix + '-' + btnName + ' ' + dropdownPrefix + ' ' + prefix + 'fixed-top',
774
+ 'data-dropdown': btnName
775
+ });
776
+ $.each(isDropdown, function (i, def) {
777
+ if (t.btnsDef[def] && t.isSupportedBtn(def)) {
778
+ $dropdown.append(t.buildSubBtn(def));
779
+ }
780
+ });
781
+ t.$box.append($dropdown.hide());
782
+ } else if (btn.key) {
783
+ t.keys[btn.key] = {
784
+ fn: btn.fn || btnName,
785
+ param: btn.param || btnName
786
+ };
787
+ }
788
+
789
+ if (!isDropdown) {
790
+ t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName;
791
+ }
792
+
793
+ return $btn;
794
+ },
795
+ // Build a button for dropdown menu
796
+ // @param n : name of the subbutton
797
+ buildSubBtn: function (btnName) {
798
+ var t = this,
799
+ prefix = t.o.prefix,
800
+ btn = t.btnsDef[btnName],
801
+ hasIcon = btn.hasIcon != null ? btn.hasIcon : true;
802
+
803
+ if (btn.key) {
804
+ t.keys[btn.key] = {
805
+ fn: btn.fn || btnName,
806
+ param: btn.param || btnName
807
+ };
808
+ }
809
+
810
+ t.tagToButton[(btn.tag || btnName).toLowerCase()] = btnName;
811
+
812
+ return $('<button/>', {
813
+ type: 'button',
814
+ class: prefix + btnName + '-dropdown-button' + (btn.ico ? ' ' + prefix + btn.ico + '-button' : ''),
815
+ html: t.hasSvg && hasIcon ? '<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) : (btn.text || btn.title || t.lang[btnName] || btnName),
816
+ title: ((btn.key) ? ' (Ctrl + ' + btn.key + ')' : null),
817
+ style: btn.style || null,
818
+ mousedown: function () {
819
+ $('body', t.doc).trigger('mousedown');
820
+
821
+ t.execCmd(btn.fn || btnName, btn.param || btnName, btn.forceCss);
822
+
823
+ return false;
824
+ }
825
+ });
826
+ },
827
+ // Check if button is supported
828
+ isSupportedBtn: function (b) {
829
+ try {
830
+ return this.btnsDef[b].isSupported();
831
+ } catch (c) {
832
+ }
833
+ return true;
834
+ },
835
+
836
+ // Build overlay for modal box
837
+ buildOverlay: function () {
838
+ var t = this;
839
+ t.$overlay = $('<div/>', {
840
+ class: t.o.prefix + 'overlay'
841
+ }).appendTo(t.$box);
842
+ return t.$overlay;
843
+ },
844
+ showOverlay: function () {
845
+ var t = this;
846
+ $(window).trigger('scroll');
847
+ t.$overlay.fadeIn(200);
848
+ t.$box.addClass(t.o.prefix + 'box-blur');
849
+ },
850
+ hideOverlay: function () {
851
+ var t = this;
852
+ t.$overlay.fadeOut(50);
853
+ t.$box.removeClass(t.o.prefix + 'box-blur');
854
+ },
855
+
856
+ // Management of fixed button pane
857
+ fixedBtnPaneEvents: function () {
858
+ var t = this,
859
+ fixedFullWidth = t.o.fixedFullWidth,
860
+ $box = t.$box;
861
+
862
+ if (!t.o.fixedBtnPane) {
863
+ return;
864
+ }
865
+
866
+ t.isFixed = false;
867
+
868
+ $(window)
869
+ .on('scroll.' + t.eventNamespace + ' resize.' + t.eventNamespace, function () {
870
+ if (!$box) {
871
+ return;
872
+ }
873
+
874
+ t.syncCode();
875
+
876
+ var scrollTop = $(window).scrollTop(),
877
+ offset = $box.offset().top + 1,
878
+ bp = t.$btnPane,
879
+ oh = bp.outerHeight() - 2;
880
+
881
+ if ((scrollTop - offset > 0) && ((scrollTop - offset - t.height) < 0)) {
882
+ if (!t.isFixed) {
883
+ t.isFixed = true;
884
+ bp.css({
885
+ position: 'fixed',
886
+ top: 0,
887
+ left: fixedFullWidth ? '0' : 'auto',
888
+ zIndex: 7
889
+ });
890
+ $([t.$ta, t.$ed]).css({marginTop: bp.height()});
891
+ }
892
+ bp.css({
893
+ width: fixedFullWidth ? '100%' : (($box.width() - 1) + 'px')
894
+ });
895
+
896
+ $('.' + t.o.prefix + 'fixed-top', $box).css({
897
+ position: fixedFullWidth ? 'fixed' : 'absolute',
898
+ top: fixedFullWidth ? oh : oh + (scrollTop - offset) + 'px',
899
+ zIndex: 15
900
+ });
901
+ } else if (t.isFixed) {
902
+ t.isFixed = false;
903
+ bp.removeAttr('style');
904
+ $([t.$ta, t.$ed]).css({marginTop: 0});
905
+ $('.' + t.o.prefix + 'fixed-top', $box).css({
906
+ position: 'absolute',
907
+ top: oh
908
+ });
909
+ }
910
+ });
911
+ },
912
+
913
+ // Disable editor
914
+ setDisabled: function (disable) {
915
+ var t = this,
916
+ prefix = t.o.prefix;
917
+
918
+ t.disabled = disable;
919
+
920
+ if (disable) {
921
+ t.$ta.attr('disabled', true);
922
+ } else {
923
+ t.$ta.removeAttr('disabled');
924
+ }
925
+ t.$box.toggleClass(prefix + 'disabled', disable);
926
+ t.$ed.attr('contenteditable', !disable);
927
+ },
928
+
929
+ // Destroy the editor
930
+ destroy: function () {
931
+ var t = this,
932
+ prefix = t.o.prefix;
933
+
934
+ if (t.isTextarea) {
935
+ t.$box.after(
936
+ t.$ta
937
+ .css({height: ''})
938
+ .val(t.html())
939
+ .removeClass(prefix + 'textarea')
940
+ .show()
941
+ );
942
+ } else {
943
+ t.$box.after(
944
+ t.$ed
945
+ .css({height: ''})
946
+ .removeClass(prefix + 'editor')
947
+ .removeAttr('contenteditable')
948
+ .removeAttr('dir')
949
+ .html(t.html())
950
+ .show()
951
+ );
952
+ }
953
+
954
+ t.$ed.off('dblclick', 'img');
955
+
956
+ t.destroyPlugins();
957
+
958
+ t.$box.remove();
959
+ t.$c.removeData('trumbowyg');
960
+ $('body').removeClass(prefix + 'body-fullscreen');
961
+ t.$c.trigger('tbwclose');
962
+ $(window).off('scroll.' + t.eventNamespace + ' resize.' + t.eventNamespace);
963
+ },
964
+
965
+
966
+ // Empty the editor
967
+ empty: function () {
968
+ this.$ta.val('');
969
+ this.syncCode(true);
970
+ },
971
+
972
+
973
+ // Function call when click on viewHTML button
974
+ toggle: function () {
975
+ var t = this,
976
+ prefix = t.o.prefix;
977
+
978
+ if (t.o.autogrowOnEnter) {
979
+ t.autogrowOnEnterDontClose = !t.$box.hasClass(prefix + 'editor-hidden');
980
+ }
981
+
982
+ t.semanticCode(false, true);
983
+
984
+ setTimeout(function () {
985
+ t.doc.activeElement.blur();
986
+ t.$box.toggleClass(prefix + 'editor-hidden ' + prefix + 'editor-visible');
987
+ t.$btnPane.toggleClass(prefix + 'disable');
988
+ $('.' + prefix + 'viewHTML-button', t.$btnPane).toggleClass(prefix + 'active');
989
+ if (t.$box.hasClass(prefix + 'editor-visible')) {
990
+ t.$ta.attr('tabindex', -1);
991
+ } else {
992
+ t.$ta.removeAttr('tabindex');
993
+ }
994
+
995
+ if (t.o.autogrowOnEnter && !t.autogrowOnEnterDontClose) {
996
+ t.autogrowEditorOnEnter();
997
+ }
998
+ }, 0);
999
+ },
1000
+
1001
+ // Open dropdown when click on a button which open that
1002
+ dropdown: function (name) {
1003
+ var t = this,
1004
+ d = t.doc,
1005
+ prefix = t.o.prefix,
1006
+ $dropdown = $('[data-dropdown=' + name + ']', t.$box),
1007
+ $btn = $('.' + prefix + name + '-button', t.$btnPane),
1008
+ show = $dropdown.is(':hidden');
1009
+
1010
+ $('body', d).trigger('mousedown');
1011
+
1012
+ if (show) {
1013
+ var o = $btn.offset().left;
1014
+ $btn.addClass(prefix + 'active');
1015
+
1016
+ $dropdown.css({
1017
+ position: 'absolute',
1018
+ top: $btn.offset().top - t.$btnPane.offset().top + $btn.outerHeight(),
1019
+ left: (t.o.fixedFullWidth && t.isFixed) ? o + 'px' : (o - t.$btnPane.offset().left) + 'px'
1020
+ }).show();
1021
+
1022
+ $(window).trigger('scroll');
1023
+
1024
+ $('body', d).on('mousedown.' + t.eventNamespace, function (e) {
1025
+ if (!$dropdown.is(e.target)) {
1026
+ $('.' + prefix + 'dropdown', d).hide();
1027
+ $('.' + prefix + 'active', d).removeClass(prefix + 'active');
1028
+ $('body', d).off('mousedown.' + t.eventNamespace);
1029
+ }
1030
+ });
1031
+ }
1032
+ },
1033
+
1034
+
1035
+ // HTML Code management
1036
+ html: function (html) {
1037
+ var t = this;
1038
+ if (html != null) {
1039
+ t.$ta.val(html);
1040
+ t.syncCode(true);
1041
+ return t;
1042
+ }
1043
+ return t.$ta.val();
1044
+ },
1045
+ syncTextarea: function () {
1046
+ var t = this;
1047
+ t.$ta.val(t.$ed.text().trim().length > 0 || t.$ed.find('hr,img,embed,iframe,input').length > 0 ? t.$ed.html() : '');
1048
+ },
1049
+ syncCode: function (force) {
1050
+ var t = this;
1051
+ if (!force && t.$ed.is(':visible')) {
1052
+ t.syncTextarea();
1053
+ } else {
1054
+ // wrap the content in a div it's easier to get the innerhtml
1055
+ var html = $('<div>').html(t.$ta.val());
1056
+ //scrub the html before loading into the doc
1057
+ var safe = $('<div>').append(html);
1058
+ $(t.o.tagsToRemove.join(','), safe).remove();
1059
+ t.$ed.html(safe.contents().html());
1060
+ }
1061
+
1062
+ if (t.o.autogrow) {
1063
+ t.height = t.$ed.height();
1064
+ if (t.height !== t.$ta.css('height')) {
1065
+ t.$ta.css({height: t.height});
1066
+ t.$c.trigger('tbwresize');
1067
+ }
1068
+ }
1069
+ if (t.o.autogrowOnEnter) {
1070
+ // t.autogrowEditorOnEnter();
1071
+ t.$ed.height('auto');
1072
+ var totalheight = t.autogrowOnEnterWasFocused ? t.$ed[0].scrollHeight : t.$ed.css('min-height');
1073
+ if (totalheight !== t.$ta.css('height')) {
1074
+ t.$ed.css({height: totalheight});
1075
+ t.$c.trigger('tbwresize');
1076
+ }
1077
+ }
1078
+ },
1079
+
1080
+ // Analyse and update to semantic code
1081
+ // @param force : force to sync code from textarea
1082
+ // @param full : wrap text nodes in <p>
1083
+ // @param keepRange : leave selection range as it is
1084
+ semanticCode: function (force, full, keepRange) {
1085
+ var t = this;
1086
+ t.saveRange();
1087
+ t.syncCode(force);
1088
+
1089
+ if (t.o.semantic) {
1090
+ t.semanticTag('b', 'strong');
1091
+ t.semanticTag('i', 'em');
1092
+ t.semanticTag('strike', 'del');
1093
+
1094
+ if (full) {
1095
+ var inlineElementsSelector = t.o.inlineElementsSelector,
1096
+ blockElementsSelector = ':not(' + inlineElementsSelector + ')';
1097
+
1098
+ // Wrap text nodes in span for easier processing
1099
+ t.$ed.contents().filter(function () {
1100
+ return this.nodeType === 3 && this.nodeValue.trim().length > 0;
1101
+ }).wrap('<span data-tbw/>');
1102
+
1103
+ // Wrap groups of inline elements in paragraphs (recursive)
1104
+ var wrapInlinesInParagraphsFrom = function ($from) {
1105
+ if ($from.length !== 0) {
1106
+ var $finalParagraph = $from.nextUntil(blockElementsSelector).addBack().wrapAll('<p/>').parent(),
1107
+ $nextElement = $finalParagraph.nextAll(inlineElementsSelector).first();
1108
+ $finalParagraph.next('br').remove();
1109
+ wrapInlinesInParagraphsFrom($nextElement);
1110
+ }
1111
+ };
1112
+ wrapInlinesInParagraphsFrom(t.$ed.children(inlineElementsSelector).first());
1113
+
1114
+ t.semanticTag('div', 'p', true);
1115
+
1116
+ // Unwrap paragraphs content, containing nothing usefull
1117
+ t.$ed.find('p').filter(function () {
1118
+ // Don't remove currently being edited element
1119
+ if (t.range && this === t.range.startContainer) {
1120
+ return false;
1121
+ }
1122
+ return $(this).text().trim().length === 0 && $(this).children().not('br,span').length === 0;
1123
+ }).contents().unwrap();
1124
+
1125
+ // Get rid of temporial span's
1126
+ $('[data-tbw]', t.$ed).contents().unwrap();
1127
+
1128
+ // Remove empty <p>
1129
+ t.$ed.find('p:empty').remove();
1130
+ }
1131
+
1132
+ if (!keepRange) {
1133
+ t.restoreRange();
1134
+ }
1135
+
1136
+ t.syncTextarea();
1137
+ }
1138
+ },
1139
+
1140
+ semanticTag: function (oldTag, newTag, copyAttributes) {
1141
+ $(oldTag, this.$ed).each(function () {
1142
+ var $oldTag = $(this);
1143
+ $oldTag.wrap('<' + newTag + '/>');
1144
+ if (copyAttributes) {
1145
+ $.each($oldTag.prop('attributes'), function () {
1146
+ $oldTag.parent().attr(this.name, this.value);
1147
+ });
1148
+ }
1149
+ $oldTag.contents().unwrap();
1150
+ });
1151
+ },
1152
+
1153
+ // Function call when user click on "Insert Link"
1154
+ createLink: function () {
1155
+ var t = this,
1156
+ documentSelection = t.doc.getSelection(),
1157
+ node = documentSelection.focusNode,
1158
+ url,
1159
+ title,
1160
+ target;
1161
+
1162
+ while (['A', 'DIV'].indexOf(node.nodeName) < 0) {
1163
+ node = node.parentNode;
1164
+ }
1165
+
1166
+ if (node && node.nodeName === 'A') {
1167
+ var $a = $(node);
1168
+ url = $a.attr('href');
1169
+ title = $a.attr('title');
1170
+ target = $a.attr('target');
1171
+ var range = t.doc.createRange();
1172
+ range.selectNode(node);
1173
+ documentSelection.removeAllRanges();
1174
+ documentSelection.addRange(range);
1175
+ }
1176
+
1177
+ t.saveRange();
1178
+
1179
+ t.openModalInsert(t.lang.createLink, {
1180
+ url: {
1181
+ label: 'URL',
1182
+ required: true,
1183
+ value: url
1184
+ },
1185
+ title: {
1186
+ label: t.lang.title,
1187
+ value: title
1188
+ },
1189
+ text: {
1190
+ label: t.lang.text,
1191
+ value: t.getRangeText()
1192
+ },
1193
+ target: {
1194
+ label: t.lang.target,
1195
+ value: target
1196
+ }
1197
+ }, function (v) { // v is value
1198
+ var link = $(['<a href="', v.url, '">', v.text, '</a>'].join(''));
1199
+ if (v.title.length > 0) {
1200
+ link.attr('title', v.title);
1201
+ }
1202
+ if (v.target.length > 0) {
1203
+ link.attr('target', v.target);
1204
+ }
1205
+ t.range.deleteContents();
1206
+ t.range.insertNode(link[0]);
1207
+ return true;
1208
+ });
1209
+ },
1210
+ unlink: function () {
1211
+ var t = this,
1212
+ documentSelection = t.doc.getSelection(),
1213
+ node = documentSelection.focusNode;
1214
+
1215
+ if (documentSelection.isCollapsed) {
1216
+ while (['A', 'DIV'].indexOf(node.nodeName) < 0) {
1217
+ node = node.parentNode;
1218
+ }
1219
+
1220
+ if (node && node.nodeName === 'A') {
1221
+ var range = t.doc.createRange();
1222
+ range.selectNode(node);
1223
+ documentSelection.removeAllRanges();
1224
+ documentSelection.addRange(range);
1225
+ }
1226
+ }
1227
+ t.execCmd('unlink', undefined, undefined, true);
1228
+ },
1229
+ insertImage: function () {
1230
+ var t = this;
1231
+ t.saveRange();
1232
+ t.openModalInsert(t.lang.insertImage, {
1233
+ url: {
1234
+ label: 'URL',
1235
+ required: true
1236
+ },
1237
+ alt: {
1238
+ label: t.lang.description,
1239
+ value: t.getRangeText()
1240
+ }
1241
+ }, function (v) { // v are values
1242
+ t.execCmd('insertImage', v.url);
1243
+ $('img[src="' + v.url + '"]:not([alt])', t.$box).attr('alt', v.alt);
1244
+ return true;
1245
+ });
1246
+ },
1247
+ fullscreen: function () {
1248
+ var t = this,
1249
+ prefix = t.o.prefix,
1250
+ fullscreenCssClass = prefix + 'fullscreen',
1251
+ isFullscreen;
1252
+
1253
+ t.$box.toggleClass(fullscreenCssClass);
1254
+ isFullscreen = t.$box.hasClass(fullscreenCssClass);
1255
+ $('body').toggleClass(prefix + 'body-fullscreen', isFullscreen);
1256
+ $(window).trigger('scroll');
1257
+ t.$c.trigger('tbw' + (isFullscreen ? 'open' : 'close') + 'fullscreen');
1258
+ },
1259
+
1260
+
1261
+ /*
1262
+ * Call method of trumbowyg if exist
1263
+ * else try to call anonymous function
1264
+ * and finaly native execCommand
1265
+ */
1266
+ execCmd: function (cmd, param, forceCss, skipTrumbowyg) {
1267
+ var t = this;
1268
+ skipTrumbowyg = !!skipTrumbowyg || '';
1269
+
1270
+ if (cmd !== 'dropdown') {
1271
+ t.$ed.focus();
1272
+ }
1273
+
1274
+ try {
1275
+ t.doc.execCommand('styleWithCSS', false, forceCss || false);
1276
+ } catch (c) {
1277
+ }
1278
+
1279
+ try {
1280
+ t[cmd + skipTrumbowyg](param);
1281
+ } catch (c) {
1282
+ try {
1283
+ cmd(param);
1284
+ } catch (e2) {
1285
+ if (cmd === 'insertHorizontalRule') {
1286
+ param = undefined;
1287
+ } else if (cmd === 'formatBlock' && t.isIE) {
1288
+ param = '<' + param + '>';
1289
+ }
1290
+
1291
+ t.doc.execCommand(cmd, false, param);
1292
+
1293
+ t.syncCode();
1294
+ t.semanticCode(false, true);
1295
+ }
1296
+
1297
+ if (cmd !== 'dropdown') {
1298
+ t.updateButtonPaneStatus();
1299
+ t.$c.trigger('tbwchange');
1300
+ }
1301
+ }
1302
+ },
1303
+
1304
+
1305
+ // Open a modal box
1306
+ openModal: function (title, content) {
1307
+ var t = this,
1308
+ prefix = t.o.prefix;
1309
+
1310
+ // No open a modal box when exist other modal box
1311
+ if ($('.' + prefix + 'modal-box', t.$box).length > 0) {
1312
+ return false;
1313
+ }
1314
+ if (t.o.autogrowOnEnter) {
1315
+ t.autogrowOnEnterDontClose = true;
1316
+ }
1317
+
1318
+ t.saveRange();
1319
+ t.showOverlay();
1320
+
1321
+ // Disable all btnPane btns
1322
+ t.$btnPane.addClass(prefix + 'disable');
1323
+
1324
+ // Build out of ModalBox, it's the mask for animations
1325
+ var $modal = $('<div/>', {
1326
+ class: prefix + 'modal ' + prefix + 'fixed-top'
1327
+ }).css({
1328
+ top: t.$btnPane.height()
1329
+ }).appendTo(t.$box);
1330
+
1331
+ // Click on overlay close modal by cancelling them
1332
+ t.$overlay.one('click', function () {
1333
+ $modal.trigger(CANCEL_EVENT);
1334
+ return false;
1335
+ });
1336
+
1337
+ // Build the form
1338
+ var $form = $('<form/>', {
1339
+ action: '',
1340
+ html: content
1341
+ })
1342
+ .on('submit', function () {
1343
+ $modal.trigger(CONFIRM_EVENT);
1344
+ return false;
1345
+ })
1346
+ .on('reset', function () {
1347
+ $modal.trigger(CANCEL_EVENT);
1348
+ return false;
1349
+ })
1350
+ .on('submit reset', function () {
1351
+ if (t.o.autogrowOnEnter) {
1352
+ t.autogrowOnEnterDontClose = false;
1353
+ }
1354
+ });
1355
+
1356
+
1357
+ // Build ModalBox and animate to show them
1358
+ var $box = $('<div/>', {
1359
+ class: prefix + 'modal-box',
1360
+ html: $form
1361
+ })
1362
+ .css({
1363
+ top: '-' + t.$btnPane.outerHeight() + 'px',
1364
+ opacity: 0
1365
+ })
1366
+ .appendTo($modal)
1367
+ .animate({
1368
+ top: 0,
1369
+ opacity: 1
1370
+ }, 100);
1371
+
1372
+
1373
+ // Append title
1374
+ $('<span/>', {
1375
+ text: title,
1376
+ class: prefix + 'modal-title'
1377
+ }).prependTo($box);
1378
+
1379
+ $modal.height($box.outerHeight() + 10);
1380
+
1381
+
1382
+ // Focus in modal box
1383
+ $('input:first', $box).focus();
1384
+
1385
+
1386
+ // Append Confirm and Cancel buttons
1387
+ t.buildModalBtn('submit', $box);
1388
+ t.buildModalBtn('reset', $box);
1389
+
1390
+
1391
+ $(window).trigger('scroll');
1392
+
1393
+ return $modal;
1394
+ },
1395
+ // @param n is name of modal
1396
+ buildModalBtn: function (n, $modal) {
1397
+ var t = this,
1398
+ prefix = t.o.prefix;
1399
+
1400
+ return $('<button/>', {
1401
+ class: prefix + 'modal-button ' + prefix + 'modal-' + n,
1402
+ type: n,
1403
+ text: t.lang[n] || n
1404
+ }).appendTo($('form', $modal));
1405
+ },
1406
+ // close current modal box
1407
+ closeModal: function () {
1408
+ var t = this,
1409
+ prefix = t.o.prefix;
1410
+
1411
+ t.$btnPane.removeClass(prefix + 'disable');
1412
+ t.$overlay.off();
1413
+
1414
+ // Find the modal box
1415
+ var $modalBox = $('.' + prefix + 'modal-box', t.$box);
1416
+
1417
+ $modalBox.animate({
1418
+ top: '-' + $modalBox.height()
1419
+ }, 100, function () {
1420
+ $modalBox.parent().remove();
1421
+ t.hideOverlay();
1422
+ });
1423
+
1424
+ t.restoreRange();
1425
+ },
1426
+ // Preformated build and management modal
1427
+ openModalInsert: function (title, fields, cmd) {
1428
+ var t = this,
1429
+ prefix = t.o.prefix,
1430
+ lg = t.lang,
1431
+ html = '';
1432
+
1433
+ $.each(fields, function (fieldName, field) {
1434
+ var l = field.label,
1435
+ n = field.name || fieldName,
1436
+ a = field.attributes || {};
1437
+
1438
+ var attr = Object.keys(a).map(function (prop) {
1439
+ return prop + '="' + a[prop] + '"';
1440
+ }).join(' ');
1441
+
1442
+ html += '<label><input type="' + (field.type || 'text') + '" name="' + n + '" value="' + (field.value || '').replace(/"/g, '&quot;') + '"' + attr + '><span class="' + prefix + 'input-infos"><span>' +
1443
+ ((!l) ? (lg[fieldName] ? lg[fieldName] : fieldName) : (lg[l] ? lg[l] : l)) +
1444
+ '</span></span></label>';
1445
+ });
1446
+
1447
+ return t.openModal(title, html)
1448
+ .on(CONFIRM_EVENT, function () {
1449
+ var $form = $('form', $(this)),
1450
+ valid = true,
1451
+ values = {};
1452
+
1453
+ $.each(fields, function (fieldName, field) {
1454
+ var $field = $('input[name="' + fieldName + '"]', $form),
1455
+ inputType = $field.attr('type');
1456
+
1457
+ if (inputType.toLowerCase() === 'checkbox') {
1458
+ values[fieldName] = $field.is(':checked');
1459
+ } else {
1460
+ values[fieldName] = $.trim($field.val());
1461
+ }
1462
+ // Validate value
1463
+ if (field.required && values[fieldName] === '') {
1464
+ valid = false;
1465
+ t.addErrorOnModalField($field, t.lang.required);
1466
+ } else if (field.pattern && !field.pattern.test(values[fieldName])) {
1467
+ valid = false;
1468
+ t.addErrorOnModalField($field, field.patternError);
1469
+ }
1470
+ });
1471
+
1472
+ if (valid) {
1473
+ t.restoreRange();
1474
+
1475
+ if (cmd(values, fields)) {
1476
+ t.syncCode();
1477
+ t.$c.trigger('tbwchange');
1478
+ t.closeModal();
1479
+ $(this).off(CONFIRM_EVENT);
1480
+ }
1481
+ }
1482
+ })
1483
+ .one(CANCEL_EVENT, function () {
1484
+ $(this).off(CONFIRM_EVENT);
1485
+ t.closeModal();
1486
+ });
1487
+ },
1488
+ addErrorOnModalField: function ($field, err) {
1489
+ var prefix = this.o.prefix,
1490
+ $label = $field.parent();
1491
+
1492
+ $field
1493
+ .on('change keyup', function () {
1494
+ $label.removeClass(prefix + 'input-error');
1495
+ });
1496
+
1497
+ $label
1498
+ .addClass(prefix + 'input-error')
1499
+ .find('input+span')
1500
+ .append(
1501
+ $('<span/>', {
1502
+ class: prefix + 'msg-error',
1503
+ text: err
1504
+ })
1505
+ );
1506
+ },
1507
+
1508
+ getDefaultImgDblClickHandler: function () {
1509
+ var t = this;
1510
+
1511
+ return function () {
1512
+ var $img = $(this),
1513
+ src = $img.attr('src'),
1514
+ base64 = '(Base64)';
1515
+
1516
+ if (src.indexOf('data:image') === 0) {
1517
+ src = base64;
1518
+ }
1519
+
1520
+ t.openModalInsert(t.lang.insertImage, {
1521
+ url: {
1522
+ label: 'URL',
1523
+ value: src,
1524
+ required: true
1525
+ },
1526
+ alt: {
1527
+ label: t.lang.description,
1528
+ value: $img.attr('alt')
1529
+ }
1530
+ }, function (v) {
1531
+ if (v.src !== base64) {
1532
+ $img.attr({
1533
+ src: v.src
1534
+ });
1535
+ }
1536
+ $img.attr({
1537
+ alt: v.alt
1538
+ });
1539
+ return true;
1540
+ });
1541
+ return false;
1542
+ };
1543
+ },
1544
+
1545
+ // Range management
1546
+ saveRange: function () {
1547
+ var t = this,
1548
+ documentSelection = t.doc.getSelection();
1549
+
1550
+ t.range = null;
1551
+
1552
+ if (documentSelection.rangeCount) {
1553
+ var savedRange = t.range = documentSelection.getRangeAt(0),
1554
+ range = t.doc.createRange(),
1555
+ rangeStart;
1556
+ range.selectNodeContents(t.$ed[0]);
1557
+ range.setEnd(savedRange.startContainer, savedRange.startOffset);
1558
+ rangeStart = (range + '').length;
1559
+ t.metaRange = {
1560
+ start: rangeStart,
1561
+ end: rangeStart + (savedRange + '').length
1562
+ };
1563
+ }
1564
+ },
1565
+ restoreRange: function () {
1566
+ var t = this,
1567
+ metaRange = t.metaRange,
1568
+ savedRange = t.range,
1569
+ documentSelection = t.doc.getSelection(),
1570
+ range;
1571
+
1572
+ if (!savedRange) {
1573
+ return;
1574
+ }
1575
+
1576
+ if (metaRange && metaRange.start !== metaRange.end) { // Algorithm from http://jsfiddle.net/WeWy7/3/
1577
+ var charIndex = 0,
1578
+ nodeStack = [t.$ed[0]],
1579
+ node,
1580
+ foundStart = false,
1581
+ stop = false;
1582
+
1583
+ range = t.doc.createRange();
1584
+
1585
+ while (!stop && (node = nodeStack.pop())) {
1586
+ if (node.nodeType === 3) {
1587
+ var nextCharIndex = charIndex + node.length;
1588
+ if (!foundStart && metaRange.start >= charIndex && metaRange.start <= nextCharIndex) {
1589
+ range.setStart(node, metaRange.start - charIndex);
1590
+ foundStart = true;
1591
+ }
1592
+ if (foundStart && metaRange.end >= charIndex && metaRange.end <= nextCharIndex) {
1593
+ range.setEnd(node, metaRange.end - charIndex);
1594
+ stop = true;
1595
+ }
1596
+ charIndex = nextCharIndex;
1597
+ } else {
1598
+ var cn = node.childNodes,
1599
+ i = cn.length;
1600
+
1601
+ while (i > 0) {
1602
+ i -= 1;
1603
+ nodeStack.push(cn[i]);
1604
+ }
1605
+ }
1606
+ }
1607
+ }
1608
+
1609
+ documentSelection.removeAllRanges();
1610
+ documentSelection.addRange(range || savedRange);
1611
+ },
1612
+ getRangeText: function () {
1613
+ return this.range + '';
1614
+ },
1615
+
1616
+ updateButtonPaneStatus: function () {
1617
+ var t = this,
1618
+ prefix = t.o.prefix,
1619
+ tags = t.getTagsRecursive(t.doc.getSelection().focusNode),
1620
+ activeClasses = prefix + 'active-button ' + prefix + 'active';
1621
+
1622
+ $('.' + prefix + 'active-button', t.$btnPane).removeClass(activeClasses);
1623
+ $.each(tags, function (i, tag) {
1624
+ var btnName = t.tagToButton[tag.toLowerCase()],
1625
+ $btn = $('.' + prefix + btnName + '-button', t.$btnPane);
1626
+
1627
+ if ($btn.length > 0) {
1628
+ $btn.addClass(activeClasses);
1629
+ } else {
1630
+ try {
1631
+ $btn = $('.' + prefix + 'dropdown .' + prefix + btnName + '-dropdown-button', t.$box);
1632
+ var dropdownBtnName = $btn.parent().data('dropdown');
1633
+ $('.' + prefix + dropdownBtnName + '-button', t.$box).addClass(activeClasses);
1634
+ } catch (e) {
1635
+ }
1636
+ }
1637
+ });
1638
+ },
1639
+ getTagsRecursive: function (element, tags) {
1640
+ var t = this;
1641
+ tags = tags || (element && element.tagName ? [element.tagName] : []);
1642
+
1643
+ if (element && element.parentNode) {
1644
+ element = element.parentNode;
1645
+ } else {
1646
+ return tags;
1647
+ }
1648
+
1649
+ var tag = element.tagName;
1650
+ if (tag === 'DIV') {
1651
+ return tags;
1652
+ }
1653
+ if (tag === 'P' && element.style.textAlign !== '') {
1654
+ tags.push(element.style.textAlign);
1655
+ }
1656
+
1657
+ $.each(t.tagHandlers, function (i, tagHandler) {
1658
+ tags = tags.concat(tagHandler(element, t));
1659
+ });
1660
+
1661
+ tags.push(tag);
1662
+
1663
+ return t.getTagsRecursive(element, tags).filter(function(tag) { return tag != null; });
1664
+ },
1665
+
1666
+ // Plugins
1667
+ initPlugins: function () {
1668
+ var t = this;
1669
+ t.loadedPlugins = [];
1670
+ $.each($.trumbowyg.plugins, function (name, plugin) {
1671
+ if (!plugin.shouldInit || plugin.shouldInit(t)) {
1672
+ plugin.init(t);
1673
+ if (plugin.tagHandler) {
1674
+ t.tagHandlers.push(plugin.tagHandler);
1675
+ }
1676
+ t.loadedPlugins.push(plugin);
1677
+ }
1678
+ });
1679
+ },
1680
+ destroyPlugins: function () {
1681
+ $.each(this.loadedPlugins, function (i, plugin) {
1682
+ if (plugin.destroy) {
1683
+ plugin.destroy();
1684
+ }
1685
+ });
1686
+ }
1687
+ };
1688
+ })(navigator, window, document, jQuery);