tramway 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +889 -15
  4. data/Rakefile +8 -8
  5. data/app/assets/config/tramway_core_manifest.js +2 -0
  6. data/app/assets/images/tramway/mona_lisa_from_prado.jpg +0 -0
  7. data/app/assets/images/tramway/mona_lisa_from_prado_square.jpg +0 -0
  8. data/app/assets/javascripts/bootstrap-datepicker-1.8.0.js +2035 -0
  9. data/app/assets/javascripts/bootstrap-datepicker-1.8.0.ru.min.js +64 -0
  10. data/app/assets/javascripts/ckeditor/config.js +30 -0
  11. data/app/assets/javascripts/ckeditor/plugins/image/dialogs/image.js +1259 -0
  12. data/app/assets/javascripts/ckeditor/plugins/image/icons/hidpi/image.png +0 -0
  13. data/app/assets/javascripts/ckeditor/plugins/image/icons/image.png +0 -0
  14. data/app/assets/javascripts/ckeditor/plugins/image/images/noimage.png +0 -0
  15. data/app/assets/javascripts/ckeditor/plugins/image/lang/en.js +25 -0
  16. data/app/assets/javascripts/ckeditor/plugins/image/lang/ru.js +25 -0
  17. data/app/assets/javascripts/ckeditor/plugins/image/plugin.js +184 -0
  18. data/app/assets/javascripts/ckeditor/plugins/youtube/images/icon-hdpi.png +0 -0
  19. data/app/assets/javascripts/ckeditor/plugins/youtube/images/icon.png +0 -0
  20. data/app/assets/javascripts/ckeditor/plugins/youtube/lang/en.js +25 -0
  21. data/app/assets/javascripts/ckeditor/plugins/youtube/lang/ru.js +25 -0
  22. data/app/assets/javascripts/ckeditor/plugins/youtube/plugin.js +449 -0
  23. data/app/assets/javascripts/tramway/application.js +60 -0
  24. data/app/assets/stylesheets/tramway/application.sass +60 -0
  25. data/app/assets/stylesheets/tramway/bootstrap-datepicker-1.8.0.css +477 -0
  26. data/app/controllers/concerns/auth_management.rb +26 -0
  27. data/app/controllers/concerns/filtering.rb +43 -0
  28. data/app/controllers/tramway/application_controller.rb +142 -0
  29. data/app/controllers/tramway/export/application_controller.rb +5 -0
  30. data/app/controllers/tramway/has_and_belongs_to_many_records_controller.rb +26 -0
  31. data/app/controllers/tramway/records_controller.rb +86 -0
  32. data/app/controllers/tramway/sessions_controller.rb +49 -0
  33. data/app/controllers/tramway/singletons_controller.rb +48 -0
  34. data/app/controllers/tramway/welcome_controller.rb +17 -0
  35. data/app/decorators/tramway/application_decorated_collection.rb +15 -0
  36. data/app/decorators/tramway/application_decorator.rb +124 -0
  37. data/app/decorators/tramway/associations/class_helper.rb +45 -0
  38. data/app/decorators/tramway/associations/object_helper.rb +58 -0
  39. data/app/decorators/tramway/attributes/view_helper.rb +30 -0
  40. data/app/decorators/tramway/concerns/attributes_decorator_helper.rb +97 -0
  41. data/app/decorators/tramway/concerns/table_builder.rb +33 -0
  42. data/app/decorators/tramway/default/values_helper.rb +23 -0
  43. data/app/decorators/tramway/delegating/class_helper.rb +9 -0
  44. data/app/decorators/tramway/user_decorator.rb +49 -0
  45. data/app/forms/admin/tramway/user_form.rb +24 -0
  46. data/app/forms/tramway/application_form.rb +124 -0
  47. data/app/forms/tramway/application_forms/association_class_helpers.rb +7 -0
  48. data/app/forms/tramway/application_forms/association_object_helpers.rb +36 -0
  49. data/app/forms/tramway/application_forms/constant_class_actions.rb +7 -0
  50. data/app/forms/tramway/application_forms/constant_object_actions.rb +20 -0
  51. data/app/forms/tramway/application_forms/frontend.rb +12 -0
  52. data/app/forms/tramway/application_forms/object_helpers.rb +15 -0
  53. data/app/forms/tramway/application_forms/properties_object_helper.rb +16 -0
  54. data/app/forms/tramway/application_forms/submit_helper.rb +26 -0
  55. data/app/forms/tramway/extendable_form.rb +15 -0
  56. data/app/forms/tramway/extendable_forms_helpers/class_builder.rb +34 -0
  57. data/app/forms/tramway/extendable_forms_helpers/ignored_properties_helper.rb +11 -0
  58. data/app/forms/tramway/extendable_forms_helpers/more_properties_helper.rb +31 -0
  59. data/app/forms/tramway/extendable_forms_helpers/properties_helper.rb +16 -0
  60. data/app/forms/tramway/extendable_forms_helpers/submit/class_helpers.rb +18 -0
  61. data/app/forms/tramway/extendable_forms_helpers/submit/object_helpers.rb +21 -0
  62. data/app/forms/tramway/extendable_forms_helpers/validators.rb +40 -0
  63. data/app/forms/tramway/extended_application_form.rb +23 -0
  64. data/app/forms/tramway/form_creator.rb +7 -0
  65. data/app/forms/tramway/session_form.rb +26 -0
  66. data/app/helpers/tramway/actions_helper.rb +45 -0
  67. data/app/helpers/tramway/additional_buttons_builder.rb +12 -0
  68. data/app/helpers/tramway/application_helper.rb +27 -0
  69. data/app/helpers/tramway/cases_helper.rb +13 -0
  70. data/app/helpers/tramway/copy_to_clipboard_helper.rb +11 -0
  71. data/app/helpers/tramway/focus_generator_helper.rb +10 -0
  72. data/app/helpers/tramway/frontend_helper.rb +26 -0
  73. data/app/helpers/tramway/inputs/associations_helper.rb +30 -0
  74. data/app/helpers/tramway/inputs/polymorphic_associations_helper.rb +24 -0
  75. data/app/helpers/tramway/inputs_helper.rb +96 -0
  76. data/app/helpers/tramway/model_helper.rb +7 -0
  77. data/app/helpers/tramway/navbar_helper.rb +11 -0
  78. data/app/helpers/tramway/records_helper.rb +120 -0
  79. data/app/helpers/tramway/russian_cases_helper.rb +26 -0
  80. data/app/helpers/tramway/singleton_helper.rb +12 -0
  81. data/app/helpers/tramway/state_machine_buttons_helper.rb +61 -0
  82. data/app/helpers/tramway/title_helper.rb +25 -0
  83. data/app/inputs/date_picker_input.rb +4 -0
  84. data/app/inputs/multiple_file_input.rb +7 -0
  85. data/app/models/tramway/application_record.rb +60 -0
  86. data/app/models/tramway/user.rb +23 -0
  87. data/app/uploaders/application_uploader.rb +22 -0
  88. data/app/uploaders/file_uploader.rb +4 -0
  89. data/app/uploaders/ico_uploader.rb +7 -0
  90. data/app/uploaders/image_defaults.rb +14 -0
  91. data/app/uploaders/photo_uploader.rb +54 -0
  92. data/app/views/layouts/tramway/application.html.haml +32 -0
  93. data/app/views/layouts/tramway/shared/_navbar.html.haml +46 -0
  94. data/app/views/tramway/404.haml +1 -0
  95. data/app/views/tramway/records/_form.html.haml +22 -0
  96. data/app/views/tramway/records/_list.html.haml +38 -0
  97. data/app/views/tramway/records/_search.html.haml +34 -0
  98. data/app/views/tramway/records/edit.html.haml +1 -0
  99. data/app/views/tramway/records/index.html.haml +31 -0
  100. data/app/views/tramway/records/new.html.haml +1 -0
  101. data/app/views/tramway/records/show.html.haml +1 -0
  102. data/app/views/tramway/sessions/new.html.haml +9 -0
  103. data/app/views/tramway/shared/_input.html.haml +34 -0
  104. data/app/views/tramway/shared/_input_extended.html.haml +14 -0
  105. data/app/views/tramway/shared/_messages.html.haml +10 -0
  106. data/app/views/tramway/shared/_show.html.haml +35 -0
  107. data/app/views/tramway/shared/errors/server_error.html.haml +12 -0
  108. data/app/views/tramway/shared/input_extended_types/_checkbox.html.haml +1 -0
  109. data/app/views/tramway/shared/input_extended_types/_select.html.haml +16 -0
  110. data/app/views/tramway/shared/input_extended_types/_simple.html.haml +4 -0
  111. data/app/views/tramway/shared/input_extended_types/_yes_no.html.haml +4 -0
  112. data/app/views/tramway/shared/show/_attribute_tr.html.haml +9 -0
  113. data/app/views/tramway/shared/show/associations/_habtm_row.html.haml +14 -0
  114. data/app/views/tramway/shared/show/associations/_list.html.haml +21 -0
  115. data/app/views/tramway/shared/show/associations/_row.html.haml +19 -0
  116. data/app/views/tramway/shared/show/associations/_table_row.html.haml +21 -0
  117. data/app/views/tramway/singletons/_form.html.haml +15 -0
  118. data/app/views/tramway/singletons/edit.html.haml +1 -0
  119. data/app/views/tramway/singletons/new.html.haml +1 -0
  120. data/app/views/tramway/singletons/show.html.haml +1 -0
  121. data/app/views/tramway/welcome/index.html.haml +4 -0
  122. data/config/initializers/assets.rb +7 -0
  123. data/config/initializers/carrierwave.rb +5 -0
  124. data/config/initializers/ckeditor.rb +9 -0
  125. data/config/initializers/plurals.rb +25 -0
  126. data/config/locales/en/collections.yml +4 -0
  127. data/config/locales/en/date.yml +16 -0
  128. data/config/locales/en/dates.yml +10 -0
  129. data/config/locales/en/helpers.yml +26 -0
  130. data/config/locales/en/locale.yml +11 -0
  131. data/config/locales/en/messages.yml +7 -0
  132. data/config/locales/en/models.yml +9 -0
  133. data/config/locales/en/simple_form_extension.yml +8 -0
  134. data/config/locales/en/state_machines.yml +8 -0
  135. data/config/locales/ru/collections.yml +4 -0
  136. data/config/locales/ru/date.yml +16 -0
  137. data/config/locales/ru/dates.yml +10 -0
  138. data/config/locales/ru/helpers.yml +30 -0
  139. data/config/locales/ru/locale.yml +6 -0
  140. data/config/locales/ru/messages.yml +7 -0
  141. data/config/locales/ru/models.yml +21 -0
  142. data/config/locales/ru/simple_form_extension.yml +8 -0
  143. data/config/locales/ru/state_machines.yml +8 -0
  144. data/config/routes.rb +13 -0
  145. data/lib/string.rb +18 -0
  146. data/lib/tramway/application.rb +10 -0
  147. data/lib/tramway/class_name_helpers.rb +15 -0
  148. data/lib/tramway/collection.rb +9 -0
  149. data/lib/tramway/collections/helper.rb +21 -0
  150. data/lib/tramway/collections.rb +4 -0
  151. data/lib/tramway/engine.rb +14 -0
  152. data/lib/tramway/error.rb +32 -0
  153. data/lib/tramway/forms.rb +5 -0
  154. data/lib/tramway/generators/install_generator.rb +49 -0
  155. data/lib/tramway/generators/model_generator.rb +105 -0
  156. data/lib/tramway/generators/templates/create_tramway_users.rb +18 -0
  157. data/lib/tramway/generators/templates/decorator.rb.erb +56 -0
  158. data/lib/tramway/generators/templates/form.rb.erb +22 -0
  159. data/lib/tramway/generators/templates/initializers/simple_form.rb +23 -0
  160. data/lib/tramway/generators/templates/initializers/simple_form_bootstrap.rb +133 -0
  161. data/lib/tramway/generators.rb +4 -0
  162. data/lib/tramway/navbar.rb +44 -0
  163. data/lib/tramway/notifications.rb +12 -0
  164. data/lib/tramway/record_routes_helper.rb +23 -0
  165. data/lib/tramway/records_models.rb +52 -0
  166. data/lib/tramway/singleton_models.rb +32 -0
  167. data/lib/tramway/spec/helpers/navbar_helper.rb +7 -0
  168. data/lib/tramway/spec/helpers/tramway_helpers.rb +47 -0
  169. data/lib/tramway/version.rb +3 -1
  170. data/lib/tramway/welcome_page_actions.rb +5 -0
  171. data/lib/tramway/yaml/errors.yml +49 -0
  172. data/lib/tramway.rb +143 -2
  173. data/lib/validators/presence_validator.rb +9 -0
  174. metadata +582 -25
  175. data/.gitignore +0 -9
  176. data/.travis.yml +0 -5
  177. data/Gemfile +0 -6
  178. data/bin/console +0 -14
  179. data/bin/setup +0 -8
  180. data/tramway.gemspec +0 -26
@@ -0,0 +1,1259 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+
6
+ ( function() {
7
+ var imageDialog = function( editor, dialogType ) {
8
+ // Load image preview.
9
+ var IMAGE = 1,
10
+ LINK = 2,
11
+ PREVIEW = 4,
12
+ CLEANUP = 8,
13
+ regexGetSize = /^\s*(\d+)((px)|\%)?\s*$/i,
14
+ regexGetSizeOrEmpty = /(^\s*(\d+)((px)|\%)?\s*$)|^$/i,
15
+ pxLengthRegex = /^\d+px$/;
16
+
17
+ var onSizeChange = function() {
18
+ var value = this.getValue(),
19
+ // This = input element.
20
+ dialog = this.getDialog(),
21
+ aMatch = value.match( regexGetSize ); // Check value
22
+ if ( aMatch ) {
23
+ if ( aMatch[ 2 ] == '%' ) // % is allowed - > unlock ratio.
24
+ switchLockRatio( dialog, false ); // Unlock.
25
+ value = aMatch[ 1 ];
26
+ }
27
+
28
+ // Only if ratio is locked
29
+ if ( dialog.lockRatio ) {
30
+ var oImageOriginal = dialog.originalElement;
31
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) {
32
+ if ( this.id == 'txtHeight' ) {
33
+ if ( value && value != '0' )
34
+ value = Math.round( oImageOriginal.$.width * ( value / oImageOriginal.$.height ) );
35
+ if ( !isNaN( value ) )
36
+ dialog.setValueOf( 'info', 'txtWidth', value );
37
+ }
38
+ // this.id = txtWidth.
39
+ else {
40
+ if ( value && value != '0' )
41
+ value = Math.round( oImageOriginal.$.height * ( value / oImageOriginal.$.width ) );
42
+ if ( !isNaN( value ) )
43
+ dialog.setValueOf( 'info', 'txtHeight', value );
44
+ }
45
+ }
46
+ }
47
+ updatePreview( dialog );
48
+ };
49
+
50
+ var updatePreview = function( dialog ) {
51
+ //Don't load before onShow.
52
+ if ( !dialog.originalElement || !dialog.preview )
53
+ return 1;
54
+
55
+ // Read attributes and update imagePreview;
56
+ dialog.commitContent( PREVIEW, dialog.preview );
57
+ return 0;
58
+ };
59
+
60
+ // Custom commit dialog logic, where we're intended to give inline style
61
+ // field (txtdlgGenStyle) higher priority to avoid overwriting styles contribute
62
+ // by other fields.
63
+ function commitContent() {
64
+ var args = arguments;
65
+ var inlineStyleField = this.getContentElement( 'advanced', 'txtdlgGenStyle' );
66
+ inlineStyleField && inlineStyleField.commit.apply( inlineStyleField, args );
67
+
68
+ this.foreach( function( widget ) {
69
+ if ( widget.commit && widget.id != 'txtdlgGenStyle' )
70
+ widget.commit.apply( widget, args );
71
+ } );
72
+ }
73
+
74
+ // Avoid recursions.
75
+ var incommit;
76
+
77
+ // Synchronous field values to other impacted fields is required, e.g. border
78
+ // size change should alter inline-style text as well.
79
+ function commitInternally( targetFields ) {
80
+ if ( incommit )
81
+ return;
82
+
83
+ incommit = 1;
84
+
85
+ var dialog = this.getDialog(),
86
+ element = dialog.imageElement;
87
+ if ( element ) {
88
+ // Commit this field and broadcast to target fields.
89
+ this.commit( IMAGE, element );
90
+
91
+ targetFields = [].concat( targetFields );
92
+ var length = targetFields.length,
93
+ field;
94
+ for ( var i = 0; i < length; i++ ) {
95
+ field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );
96
+ // May cause recursion.
97
+ field && field.setup( IMAGE, element );
98
+ }
99
+ }
100
+
101
+ incommit = 0;
102
+ }
103
+
104
+ var switchLockRatio = function( dialog, value ) {
105
+ if ( !dialog.getContentElement( 'info', 'ratioLock' ) )
106
+ return null;
107
+
108
+ var oImageOriginal = dialog.originalElement;
109
+
110
+ // Dialog may already closed. (https://dev.ckeditor.com/ticket/5505)
111
+ if ( !oImageOriginal )
112
+ return null;
113
+
114
+ // Check image ratio and original image ratio, but respecting user's preference.
115
+ if ( value == 'check' ) {
116
+ if ( !dialog.userlockRatio && oImageOriginal.getCustomData( 'isReady' ) == 'true' ) {
117
+ var width = dialog.getValueOf( 'info', 'txtWidth' ),
118
+ height = dialog.getValueOf( 'info', 'txtHeight' ),
119
+ originalRatio = oImageOriginal.$.width / oImageOriginal.$.height,
120
+ thisRatio = width / height;
121
+
122
+ dialog.lockRatio = false; // Default: unlock ratio
123
+
124
+ if ( !width && !height ) {
125
+ dialog.lockRatio = true;
126
+ } else {
127
+ // Round ratio to two decimal places so ratio locking will be less precise (#2254).
128
+ var ratioComparison = Math.round( ( originalRatio / thisRatio ) * 100 ) / 100;
129
+
130
+ if ( ratioComparison == 1 ) {
131
+ dialog.lockRatio = true;
132
+ }
133
+ }
134
+ }
135
+ } else if ( value !== undefined ) {
136
+ dialog.lockRatio = value;
137
+ } else {
138
+ dialog.userlockRatio = 1;
139
+ dialog.lockRatio = !dialog.lockRatio;
140
+ }
141
+
142
+ var ratioButton = CKEDITOR.document.getById( btnLockSizesId );
143
+ if ( dialog.lockRatio )
144
+ ratioButton.removeClass( 'cke_btn_unlocked' );
145
+ else
146
+ ratioButton.addClass( 'cke_btn_unlocked' );
147
+
148
+ ratioButton.setAttribute( 'aria-checked', dialog.lockRatio );
149
+
150
+ // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE
151
+ if ( CKEDITOR.env.hc ) {
152
+ var icon = ratioButton.getChild( 0 );
153
+ icon.setHtml( dialog.lockRatio ? CKEDITOR.env.ie ? '\u25A0' : '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' );
154
+ }
155
+
156
+ return dialog.lockRatio;
157
+ };
158
+
159
+ var resetSize = function( dialog, emptyValues ) {
160
+ var oImageOriginal = dialog.originalElement,
161
+ ready = oImageOriginal.getCustomData( 'isReady' ) == 'true';
162
+
163
+ if ( ready ) {
164
+ var widthField = dialog.getContentElement( 'info', 'txtWidth' ),
165
+ heightField = dialog.getContentElement( 'info', 'txtHeight' ),
166
+ widthValue, heightValue;
167
+
168
+ if ( emptyValues ) {
169
+ widthValue = 0;
170
+ heightValue = 0;
171
+ } else {
172
+ widthValue = oImageOriginal.$.width;
173
+ heightValue = oImageOriginal.$.height;
174
+ }
175
+
176
+ widthField && widthField.setValue( widthValue );
177
+ heightField && heightField.setValue( heightValue );
178
+ }
179
+ updatePreview( dialog );
180
+ };
181
+
182
+ var setupDimension = function( type, element ) {
183
+ if ( type != IMAGE )
184
+ return;
185
+
186
+ function checkDimension( size, defaultValue ) {
187
+ var aMatch = size.match( regexGetSize );
188
+ if ( aMatch ) {
189
+ // % is allowed.
190
+ if ( aMatch[ 2 ] == '%' ) {
191
+ aMatch[ 1 ] += '%';
192
+ switchLockRatio( dialog, false ); // Unlock ratio
193
+ }
194
+ return aMatch[ 1 ];
195
+ }
196
+ return defaultValue;
197
+ }
198
+
199
+ var dialog = this.getDialog(),
200
+ value = '',
201
+ dimension = this.id == 'txtWidth' ? 'width' : 'height',
202
+ size = element.getAttribute( dimension );
203
+
204
+ if ( size )
205
+ value = checkDimension( size, value );
206
+ value = checkDimension( element.getStyle( dimension ), value );
207
+
208
+ this.setValue( value );
209
+ };
210
+
211
+ var previewPreloader;
212
+
213
+ var onImgLoadEvent = function() {
214
+ // Image is ready.
215
+ var original = this.originalElement,
216
+ loader = CKEDITOR.document.getById( imagePreviewLoaderId );
217
+
218
+ original.setCustomData( 'isReady', 'true' );
219
+ original.removeListener( 'load', onImgLoadEvent );
220
+ original.removeListener( 'error', onImgLoadErrorEvent );
221
+ original.removeListener( 'abort', onImgLoadErrorEvent );
222
+
223
+ // Hide loader.
224
+ if ( loader )
225
+ loader.setStyle( 'display', 'none' );
226
+
227
+ // New image -> new dimensions
228
+ if ( !this.dontResetSize ) {
229
+ resetSize( this, editor.config.image_prefillDimensions === false );
230
+ }
231
+
232
+ if ( this.firstLoad ) {
233
+ CKEDITOR.tools.setTimeout( function() {
234
+ switchLockRatio( this, 'check' );
235
+ }, 0, this );
236
+ }
237
+
238
+ this.firstLoad = false;
239
+ this.dontResetSize = false;
240
+
241
+ // Possible fix for https://dev.ckeditor.com/ticket/12818.
242
+ updatePreview( this );
243
+ };
244
+
245
+ var onImgLoadErrorEvent = function() {
246
+ // Error. Image is not loaded.
247
+ var original = this.originalElement,
248
+ loader = CKEDITOR.document.getById( imagePreviewLoaderId );
249
+
250
+ original.removeListener( 'load', onImgLoadEvent );
251
+ original.removeListener( 'error', onImgLoadErrorEvent );
252
+ original.removeListener( 'abort', onImgLoadErrorEvent );
253
+
254
+ // Set Error image.
255
+ var noimage = CKEDITOR.getUrl( CKEDITOR.plugins.get( 'image' ).path + 'images/noimage.png' );
256
+
257
+ if ( this.preview )
258
+ this.preview.setAttribute( 'src', noimage );
259
+
260
+ // Hide loader.
261
+ if ( loader )
262
+ loader.setStyle( 'display', 'none' );
263
+
264
+ switchLockRatio( this, false ); // Unlock.
265
+ };
266
+
267
+ var numbering = function( id ) {
268
+ return CKEDITOR.tools.getNextId() + '_' + id;
269
+ },
270
+ btnLockSizesId = numbering( 'btnLockSizes' ),
271
+ btnResetSizeId = numbering( 'btnResetSize' ),
272
+ imagePreviewLoaderId = numbering( 'ImagePreviewLoader' ),
273
+ previewLinkId = numbering( 'previewLink' ),
274
+ previewImageId = numbering( 'previewImage' );
275
+
276
+ return {
277
+ title: editor.lang.image[ dialogType == 'image' ? 'title' : 'titleButton' ],
278
+ minWidth: ( CKEDITOR.skinName || editor.config.skin ) == 'moono-lisa' ? 500 : 420,
279
+ minHeight: 360,
280
+ onShow: function() {
281
+ this.imageElement = false;
282
+ this.linkElement = false;
283
+
284
+ // Default: create a new element.
285
+ this.imageEditMode = false;
286
+ this.linkEditMode = false;
287
+
288
+ this.lockRatio = true;
289
+ this.userlockRatio = 0;
290
+ this.dontResetSize = false;
291
+ this.firstLoad = true;
292
+ this.addLink = false;
293
+
294
+ var editor = this.getParentEditor(),
295
+ sel = editor.getSelection(),
296
+ element = sel && sel.getSelectedElement(),
297
+ link = element && editor.elementPath( element ).contains( 'a', 1 ),
298
+ loader = CKEDITOR.document.getById( imagePreviewLoaderId );
299
+
300
+ // Hide loader.
301
+ if ( loader )
302
+ loader.setStyle( 'display', 'none' );
303
+
304
+ // Create the preview before setup the dialog contents.
305
+ previewPreloader = new CKEDITOR.dom.element( 'img', editor.document );
306
+ this.preview = CKEDITOR.document.getById( previewImageId );
307
+
308
+ // Copy of the image
309
+ this.originalElement = editor.document.createElement( 'img' );
310
+ this.originalElement.setAttribute( 'alt', '' );
311
+ this.originalElement.setCustomData( 'isReady', 'false' );
312
+
313
+ if ( link ) {
314
+ this.linkElement = link;
315
+ this.linkEditMode = true;
316
+
317
+ // If there is an existing link, by default keep it (true).
318
+ // It will be removed if certain conditions are met and Link tab is enabled. (https://dev.ckeditor.com/ticket/13351)
319
+ this.addLink = true;
320
+
321
+ // Look for Image element.
322
+ var linkChildren = link.getChildren();
323
+ if ( linkChildren.count() == 1 ) {
324
+ var childTag = linkChildren.getItem( 0 );
325
+
326
+ if ( childTag.type == CKEDITOR.NODE_ELEMENT ) {
327
+ if ( childTag.is( 'img' ) || childTag.is( 'input' ) ) {
328
+ this.imageElement = linkChildren.getItem( 0 );
329
+ if ( this.imageElement.is( 'img' ) )
330
+ this.imageEditMode = 'img';
331
+ else if ( this.imageElement.is( 'input' ) )
332
+ this.imageEditMode = 'input';
333
+ }
334
+ }
335
+ }
336
+ // Fill out all fields.
337
+ if ( dialogType == 'image' )
338
+ this.setupContent( LINK, link );
339
+ }
340
+
341
+ // Edit given image element instead the one from selection.
342
+ if ( this.customImageElement ) {
343
+ this.imageEditMode = 'img';
344
+ this.imageElement = this.customImageElement;
345
+ delete this.customImageElement;
346
+ }
347
+ else if ( element && element.getName() == 'img' && !element.data( 'cke-realelement' ) ||
348
+ element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'image' ) {
349
+ this.imageEditMode = element.getName();
350
+ this.imageElement = element;
351
+ }
352
+
353
+ if ( this.imageEditMode ) {
354
+ // Use the original element as a buffer from since we don't want
355
+ // temporary changes to be committed, e.g. if the dialog is canceled.
356
+ this.cleanImageElement = this.imageElement;
357
+ this.imageElement = this.cleanImageElement.clone( true, true );
358
+
359
+ // Fill out all fields.
360
+ this.setupContent( IMAGE, this.imageElement );
361
+ }
362
+
363
+ // Refresh LockRatio button
364
+ switchLockRatio( this, true );
365
+
366
+ // Dont show preview if no URL given.
367
+ if ( !CKEDITOR.tools.trim( this.getValueOf( 'info', 'txtUrl' ) ) ) {
368
+ this.preview.removeAttribute( 'src' );
369
+ this.preview.setStyle( 'display', 'none' );
370
+ }
371
+ },
372
+ onOk: function() {
373
+ // Edit existing Image.
374
+ if ( this.imageEditMode ) {
375
+ var imgTagName = this.imageEditMode;
376
+
377
+ // Image dialog and Input element.
378
+ if ( dialogType == 'image' && imgTagName == 'input' && confirm( editor.lang.image.button2Img ) ) { // jshint ignore:line
379
+ // Replace INPUT-> IMG
380
+ imgTagName = 'img';
381
+ this.imageElement = editor.document.createElement( 'img' );
382
+ this.imageElement.setAttribute( 'alt', '' );
383
+ editor.insertElement( this.imageElement );
384
+ }
385
+ // ImageButton dialog and Image element.
386
+ else if ( dialogType != 'image' && imgTagName == 'img' && confirm( editor.lang.image.img2Button ) ) { // jshint ignore:line
387
+ // Replace IMG -> INPUT
388
+ imgTagName = 'input';
389
+ this.imageElement = editor.document.createElement( 'input' );
390
+ this.imageElement.setAttributes( {
391
+ type: 'image',
392
+ alt: ''
393
+ } );
394
+ editor.insertElement( this.imageElement );
395
+ } else {
396
+ // Restore the original element before all commits.
397
+ this.imageElement = this.cleanImageElement;
398
+ delete this.cleanImageElement;
399
+ }
400
+ }
401
+ // Create a new image.
402
+ else {
403
+ // Image dialog -> create IMG element.
404
+ if ( dialogType == 'image' )
405
+ this.imageElement = editor.document.createElement( 'img' );
406
+ else {
407
+ this.imageElement = editor.document.createElement( 'input' );
408
+ this.imageElement.setAttribute( 'type', 'image' );
409
+ }
410
+ this.imageElement.setAttribute( 'alt', '' );
411
+ }
412
+
413
+ // Create a new link.
414
+ if ( !this.linkEditMode )
415
+ this.linkElement = editor.document.createElement( 'a' );
416
+
417
+ // Set attributes.
418
+ this.commitContent( IMAGE, this.imageElement );
419
+ this.commitContent( LINK, this.linkElement );
420
+
421
+ // Remove empty style attribute.
422
+ if ( !this.imageElement.getAttribute( 'style' ) )
423
+ this.imageElement.removeAttribute( 'style' );
424
+
425
+ // Insert a new Image.
426
+ if ( !this.imageEditMode ) {
427
+ if ( this.addLink ) {
428
+ if ( !this.linkEditMode ) {
429
+ // Insert a new link.
430
+ editor.insertElement( this.linkElement );
431
+ this.linkElement.append( this.imageElement, false );
432
+ } else {
433
+ // We already have a link in editor.
434
+ if ( this.linkElement.equals( editor.getSelection().getSelectedElement() ) ) {
435
+ // If the link is selected outside, replace it's content rather than the link itself. ([<a>foo</a>])
436
+ this.linkElement.setHtml( '' );
437
+ this.linkElement.append( this.imageElement, false );
438
+ } else {
439
+ // Only inside of the link is selected, so replace it with image. (<a>[foo]</a>, <a>[f]oo</a>)
440
+ editor.insertElement( this.imageElement );
441
+ }
442
+ }
443
+ } else {
444
+ editor.insertElement( this.imageElement );
445
+ }
446
+ }
447
+ // Image already exists.
448
+ else {
449
+ // Add a new link element.
450
+ if ( !this.linkEditMode && this.addLink ) {
451
+ editor.insertElement( this.linkElement );
452
+ this.imageElement.appendTo( this.linkElement );
453
+ }
454
+ // Remove Link, Image exists.
455
+ else if ( this.linkEditMode && !this.addLink ) {
456
+ editor.getSelection().selectElement( this.linkElement );
457
+ editor.insertElement( this.imageElement );
458
+ }
459
+ }
460
+ },
461
+ onLoad: function() {
462
+ if ( dialogType != 'image' )
463
+ this.hidePage( 'Link' ); //Hide Link tab.
464
+ var doc = this._.element.getDocument();
465
+
466
+ if ( this.getContentElement( 'info', 'ratioLock' ) ) {
467
+ this.addFocusable( doc.getById( btnResetSizeId ), 5 );
468
+ this.addFocusable( doc.getById( btnLockSizesId ), 5 );
469
+ }
470
+
471
+ this.commitContent = commitContent;
472
+ },
473
+ onHide: function() {
474
+ if ( this.preview )
475
+ this.commitContent( CLEANUP, this.preview );
476
+
477
+ if ( this.originalElement ) {
478
+ this.originalElement.removeListener( 'load', onImgLoadEvent );
479
+ this.originalElement.removeListener( 'error', onImgLoadErrorEvent );
480
+ this.originalElement.removeListener( 'abort', onImgLoadErrorEvent );
481
+ this.originalElement.remove();
482
+ this.originalElement = false; // Dialog is closed.
483
+ }
484
+
485
+ delete this.imageElement;
486
+ },
487
+ contents: [ {
488
+ id: 'info',
489
+ label: editor.lang.image.infoTab,
490
+ accessKey: 'I',
491
+ elements: [ {
492
+ type: 'vbox',
493
+ padding: 0,
494
+ children: [ {
495
+ type: 'hbox',
496
+ widths: [ '280px', '110px' ],
497
+ align: 'right',
498
+ className: 'cke_dialog_image_url',
499
+ children: [ {
500
+ id: 'txtUrl',
501
+ type: 'text',
502
+ label: editor.lang.common.url,
503
+ required: true,
504
+ onChange: function() {
505
+ var dialog = this.getDialog(),
506
+ newUrl = this.getValue();
507
+
508
+ // Update original image.
509
+ // Prevent from load before onShow.
510
+ if ( newUrl.length > 0 ) {
511
+ dialog = this.getDialog();
512
+ var original = dialog.originalElement;
513
+
514
+ if ( dialog.preview ) {
515
+ dialog.preview.removeStyle( 'display' );
516
+ }
517
+
518
+ original.setCustomData( 'isReady', 'false' );
519
+ // Show loader.
520
+ var loader = CKEDITOR.document.getById( imagePreviewLoaderId );
521
+ if ( loader )
522
+ loader.setStyle( 'display', '' );
523
+
524
+ original.on( 'load', onImgLoadEvent, dialog );
525
+ original.on( 'error', onImgLoadErrorEvent, dialog );
526
+ original.on( 'abort', onImgLoadErrorEvent, dialog );
527
+ original.setAttribute( 'src', newUrl );
528
+
529
+ if ( dialog.preview ) {
530
+ // Query the preloader to figure out the url impacted by based href.
531
+ previewPreloader.setAttribute( 'src', newUrl );
532
+ dialog.preview.setAttribute( 'src', previewPreloader.$.src );
533
+ updatePreview( dialog );
534
+ }
535
+ }
536
+ // Dont show preview if no URL given.
537
+ else if ( dialog.preview ) {
538
+ dialog.preview.removeAttribute( 'src' );
539
+ dialog.preview.setStyle( 'display', 'none' );
540
+ }
541
+ },
542
+ setup: function( type, element ) {
543
+ if ( type == IMAGE ) {
544
+ var url = element.data( 'cke-saved-src' ) || element.getAttribute( 'src' );
545
+ var field = this;
546
+
547
+ this.getDialog().dontResetSize = true;
548
+
549
+ field.setValue( url ); // And call this.onChange()
550
+ // Manually set the initial value.(https://dev.ckeditor.com/ticket/4191)
551
+ field.setInitValue();
552
+ }
553
+ },
554
+ commit: function( type, element ) {
555
+ if ( type == IMAGE && ( this.getValue() || this.isChanged() ) ) {
556
+ element.data( 'cke-saved-src', this.getValue() );
557
+ element.setAttribute( 'src', this.getValue() );
558
+ } else if ( type == CLEANUP ) {
559
+ element.setAttribute( 'src', '' ); // If removeAttribute doesn't work.
560
+ element.removeAttribute( 'src' );
561
+ }
562
+ },
563
+ validate: CKEDITOR.dialog.validate.notEmpty( editor.lang.image.urlMissing )
564
+ },
565
+ {
566
+ type: 'button',
567
+ id: 'browse',
568
+ // v-align with the 'txtUrl' field.
569
+ // TODO: We need something better than a fixed size here.
570
+ style: 'display:inline-block;margin-top:14px;',
571
+ align: 'center',
572
+ label: editor.lang.common.browseServer,
573
+ hidden: true,
574
+ filebrowser: 'info:txtUrl'
575
+ } ]
576
+ } ]
577
+ },
578
+ {
579
+ id: 'txtAlt',
580
+ type: 'text',
581
+ label: editor.lang.image.alt,
582
+ accessKey: 'T',
583
+ 'default': '',
584
+ onChange: function() {
585
+ updatePreview( this.getDialog() );
586
+ },
587
+ setup: function( type, element ) {
588
+ if ( type == IMAGE )
589
+ this.setValue( element.getAttribute( 'alt' ) );
590
+ },
591
+ commit: function( type, element ) {
592
+ if ( type == IMAGE ) {
593
+ if ( this.getValue() || this.isChanged() )
594
+ element.setAttribute( 'alt', this.getValue() );
595
+ } else if ( type == PREVIEW )
596
+ element.setAttribute( 'alt', this.getValue() );
597
+ else if ( type == CLEANUP ) {
598
+ element.removeAttribute( 'alt' );
599
+ }
600
+
601
+ }
602
+ },
603
+ {
604
+ type: 'hbox',
605
+ children: [ {
606
+ id: 'basic',
607
+ type: 'vbox',
608
+ children: [ {
609
+ type: 'hbox',
610
+ requiredContent: 'img{width,height}',
611
+ widths: [ '50%', '50%' ],
612
+ children: [ {
613
+ type: 'vbox',
614
+ padding: 1,
615
+ children: [ {
616
+ type: 'text',
617
+ width: '45px',
618
+ id: 'txtWidth',
619
+ label: editor.lang.common.width,
620
+ onKeyUp: onSizeChange,
621
+ onChange: function() {
622
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
623
+ },
624
+ validate: function() {
625
+ var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
626
+ isValid = !!( aMatch && parseInt( aMatch[ 1 ], 10 ) !== 0 );
627
+ if ( !isValid )
628
+ alert( editor.lang.common.invalidLength.replace( '%1', editor.lang.common.width ).replace( '%2', 'px, %' ) ); // jshint ignore:line
629
+ return isValid;
630
+ },
631
+ setup: setupDimension,
632
+ commit: function( type, element ) {
633
+ var value = this.getValue();
634
+ if ( type == IMAGE ) {
635
+ if ( value && editor.activeFilter.check( 'img{width,height}' ) )
636
+ element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
637
+ else
638
+ element.removeStyle( 'width' );
639
+
640
+ element.removeAttribute( 'width' );
641
+ } else if ( type == PREVIEW ) {
642
+ var aMatch = value.match( regexGetSize );
643
+ if ( !aMatch ) {
644
+ var oImageOriginal = this.getDialog().originalElement;
645
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
646
+ element.setStyle( 'width', oImageOriginal.$.width + 'px' );
647
+ } else {
648
+ element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
649
+ }
650
+ } else if ( type == CLEANUP ) {
651
+ element.removeAttribute( 'width' );
652
+ element.removeStyle( 'width' );
653
+ }
654
+ }
655
+ },
656
+ {
657
+ type: 'text',
658
+ id: 'txtHeight',
659
+ width: '45px',
660
+ label: editor.lang.common.height,
661
+ onKeyUp: onSizeChange,
662
+ onChange: function() {
663
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
664
+ },
665
+ validate: function() {
666
+ var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
667
+ isValid = !!( aMatch && parseInt( aMatch[ 1 ], 10 ) !== 0 );
668
+ if ( !isValid )
669
+ alert( editor.lang.common.invalidLength.replace( '%1', editor.lang.common.height ).replace( '%2', 'px, %' ) ); // jshint ignore:line
670
+ return isValid;
671
+ },
672
+ setup: setupDimension,
673
+ commit: function( type, element ) {
674
+ var value = this.getValue();
675
+ if ( type == IMAGE ) {
676
+ if ( value && editor.activeFilter.check( 'img{width,height}' ) )
677
+ element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
678
+ else
679
+ element.removeStyle( 'height' );
680
+
681
+ element.removeAttribute( 'height' );
682
+ } else if ( type == PREVIEW ) {
683
+ var aMatch = value.match( regexGetSize );
684
+ if ( !aMatch ) {
685
+ var oImageOriginal = this.getDialog().originalElement;
686
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
687
+ element.setStyle( 'height', oImageOriginal.$.height + 'px' );
688
+ } else {
689
+ element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
690
+ }
691
+ } else if ( type == CLEANUP ) {
692
+ element.removeAttribute( 'height' );
693
+ element.removeStyle( 'height' );
694
+ }
695
+ }
696
+ } ]
697
+ },
698
+ {
699
+ id: 'ratioLock',
700
+ type: 'html',
701
+ className: 'cke_dialog_image_ratiolock',
702
+ style: 'margin-top:30px;width:40px;height:40px;',
703
+ onLoad: function() {
704
+ // Activate Reset button
705
+ var resetButton = CKEDITOR.document.getById( btnResetSizeId ),
706
+ ratioButton = CKEDITOR.document.getById( btnLockSizesId );
707
+ if ( resetButton ) {
708
+ resetButton.on( 'click', function( evt ) {
709
+ resetSize( this );
710
+ evt.data && evt.data.preventDefault();
711
+ }, this.getDialog() );
712
+ resetButton.on( 'mouseover', function() {
713
+ this.addClass( 'cke_btn_over' );
714
+ }, resetButton );
715
+ resetButton.on( 'mouseout', function() {
716
+ this.removeClass( 'cke_btn_over' );
717
+ }, resetButton );
718
+ }
719
+ // Activate (Un)LockRatio button
720
+ if ( ratioButton ) {
721
+ ratioButton.on( 'click', function( evt ) {
722
+ switchLockRatio( this );
723
+
724
+ var oImageOriginal = this.originalElement,
725
+ width = this.getValueOf( 'info', 'txtWidth' );
726
+
727
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' && width ) {
728
+ var height = oImageOriginal.$.height / oImageOriginal.$.width * width;
729
+ if ( !isNaN( height ) ) {
730
+ this.setValueOf( 'info', 'txtHeight', Math.round( height ) );
731
+ updatePreview( this );
732
+ }
733
+ }
734
+ evt.data && evt.data.preventDefault();
735
+ }, this.getDialog() );
736
+ ratioButton.on( 'mouseover', function() {
737
+ this.addClass( 'cke_btn_over' );
738
+ }, ratioButton );
739
+ ratioButton.on( 'mouseout', function() {
740
+ this.removeClass( 'cke_btn_over' );
741
+ }, ratioButton );
742
+ }
743
+ },
744
+ html: '<div>' +
745
+ '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.lockRatio +
746
+ '" class="cke_btn_locked" id="' + btnLockSizesId + '" role="checkbox"><span class="cke_icon"></span><span class="cke_label">' + editor.lang.image.lockRatio + '</span></a>' +
747
+ '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.resetSize +
748
+ '" class="cke_btn_reset" id="' + btnResetSizeId + '" role="button"><span class="cke_label">' + editor.lang.image.resetSize + '</span></a>' +
749
+ '</div>'
750
+ } ]
751
+ },
752
+ {
753
+ type: 'vbox',
754
+ padding: 1,
755
+ children: [ {
756
+ type: 'text',
757
+ id: 'txtBorder',
758
+ requiredContent: 'img{border-width}',
759
+ width: '60px',
760
+ label: editor.lang.image.border,
761
+ 'default': '',
762
+ onKeyUp: function() {
763
+ updatePreview( this.getDialog() );
764
+ },
765
+ onChange: function() {
766
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
767
+ },
768
+ validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateBorder ),
769
+ setup: function( type, element ) {
770
+ if ( type == IMAGE ) {
771
+ var value,
772
+ borderStyle = element.getStyle( 'border-width' );
773
+ borderStyle = borderStyle && borderStyle.match( /^(\d+px)(?: \1 \1 \1)?$/ );
774
+ value = borderStyle && parseInt( borderStyle[ 1 ], 10 );
775
+ isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'border' ) );
776
+ this.setValue( value );
777
+ }
778
+ },
779
+ commit: function( type, element ) {
780
+ var value = parseInt( this.getValue(), 10 );
781
+ if ( type == IMAGE || type == PREVIEW ) {
782
+ if ( !isNaN( value ) ) {
783
+ element.setStyle( 'border-width', CKEDITOR.tools.cssLength( value ) );
784
+ element.setStyle( 'border-style', 'solid' );
785
+ } else if ( !value && this.isChanged() ) {
786
+ element.removeStyle( 'border' );
787
+ }
788
+
789
+ if ( type == IMAGE )
790
+ element.removeAttribute( 'border' );
791
+ } else if ( type == CLEANUP ) {
792
+ element.removeAttribute( 'border' );
793
+ element.removeStyle( 'border-width' );
794
+ element.removeStyle( 'border-style' );
795
+ element.removeStyle( 'border-color' );
796
+ }
797
+ }
798
+ },
799
+ {
800
+ type: 'text',
801
+ id: 'txtHSpace',
802
+ requiredContent: 'img{margin-left,margin-right}',
803
+ width: '60px',
804
+ label: editor.lang.image.hSpace,
805
+ 'default': '',
806
+ onKeyUp: function() {
807
+ updatePreview( this.getDialog() );
808
+ },
809
+ onChange: function() {
810
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
811
+ },
812
+ validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateHSpace ),
813
+ setup: function( type, element ) {
814
+ if ( type == IMAGE ) {
815
+ var value, marginLeftPx, marginRightPx,
816
+ marginLeftStyle = element.getStyle( 'margin-left' ),
817
+ marginRightStyle = element.getStyle( 'margin-right' );
818
+
819
+ marginLeftStyle = marginLeftStyle && marginLeftStyle.match( pxLengthRegex );
820
+ marginRightStyle = marginRightStyle && marginRightStyle.match( pxLengthRegex );
821
+ marginLeftPx = parseInt( marginLeftStyle, 10 );
822
+ marginRightPx = parseInt( marginRightStyle, 10 );
823
+
824
+ value = ( marginLeftPx == marginRightPx ) && marginLeftPx;
825
+ isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'hspace' ) );
826
+
827
+ this.setValue( value );
828
+ }
829
+ },
830
+ commit: function( type, element ) {
831
+ var value = parseInt( this.getValue(), 10 );
832
+ if ( type == IMAGE || type == PREVIEW ) {
833
+ if ( !isNaN( value ) ) {
834
+ element.setStyle( 'margin-left', CKEDITOR.tools.cssLength( value ) );
835
+ element.setStyle( 'margin-right', CKEDITOR.tools.cssLength( value ) );
836
+ } else if ( !value && this.isChanged() ) {
837
+ element.removeStyle( 'margin-left' );
838
+ element.removeStyle( 'margin-right' );
839
+ }
840
+
841
+ if ( type == IMAGE )
842
+ element.removeAttribute( 'hspace' );
843
+ } else if ( type == CLEANUP ) {
844
+ element.removeAttribute( 'hspace' );
845
+ element.removeStyle( 'margin-left' );
846
+ element.removeStyle( 'margin-right' );
847
+ }
848
+ }
849
+ },
850
+ {
851
+ type: 'text',
852
+ id: 'txtVSpace',
853
+ requiredContent: 'img{margin-top,margin-bottom}',
854
+ width: '60px',
855
+ label: editor.lang.image.vSpace,
856
+ 'default': '',
857
+ onKeyUp: function() {
858
+ updatePreview( this.getDialog() );
859
+ },
860
+ onChange: function() {
861
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
862
+ },
863
+ validate: CKEDITOR.dialog.validate.integer( editor.lang.image.validateVSpace ),
864
+ setup: function( type, element ) {
865
+ if ( type == IMAGE ) {
866
+ var value, marginTopPx, marginBottomPx,
867
+ marginTopStyle = element.getStyle( 'margin-top' ),
868
+ marginBottomStyle = element.getStyle( 'margin-bottom' );
869
+
870
+ marginTopStyle = marginTopStyle && marginTopStyle.match( pxLengthRegex );
871
+ marginBottomStyle = marginBottomStyle && marginBottomStyle.match( pxLengthRegex );
872
+ marginTopPx = parseInt( marginTopStyle, 10 );
873
+ marginBottomPx = parseInt( marginBottomStyle, 10 );
874
+
875
+ value = ( marginTopPx == marginBottomPx ) && marginTopPx;
876
+ isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'vspace' ) );
877
+ this.setValue( value );
878
+ }
879
+ },
880
+ commit: function( type, element ) {
881
+ var value = parseInt( this.getValue(), 10 );
882
+ if ( type == IMAGE || type == PREVIEW ) {
883
+ if ( !isNaN( value ) ) {
884
+ element.setStyle( 'margin-top', CKEDITOR.tools.cssLength( value ) );
885
+ element.setStyle( 'margin-bottom', CKEDITOR.tools.cssLength( value ) );
886
+ } else if ( !value && this.isChanged() ) {
887
+ element.removeStyle( 'margin-top' );
888
+ element.removeStyle( 'margin-bottom' );
889
+ }
890
+
891
+ if ( type == IMAGE )
892
+ element.removeAttribute( 'vspace' );
893
+ } else if ( type == CLEANUP ) {
894
+ element.removeAttribute( 'vspace' );
895
+ element.removeStyle( 'margin-top' );
896
+ element.removeStyle( 'margin-bottom' );
897
+ }
898
+ }
899
+ },
900
+ {
901
+ id: 'cmbAlign',
902
+ requiredContent: 'img{float}',
903
+ type: 'select',
904
+ widths: [ '35%', '65%' ],
905
+ style: 'width:90px',
906
+ label: editor.lang.common.align,
907
+ 'default': '',
908
+ items: [
909
+ [ editor.lang.common.notSet, '' ],
910
+ [ editor.lang.common.left, 'left' ],
911
+ [ editor.lang.common.right, 'right' ]
912
+ // Backward compatible with v2 on setup when specified as attribute value,
913
+ // while these values are no more available as select options.
914
+ // [ editor.lang.image.alignAbsBottom , 'absBottom'],
915
+ // [ editor.lang.image.alignAbsMiddle , 'absMiddle'],
916
+ // [ editor.lang.image.alignBaseline , 'baseline'],
917
+ // [ editor.lang.image.alignTextTop , 'text-top'],
918
+ // [ editor.lang.image.alignBottom , 'bottom'],
919
+ // [ editor.lang.image.alignMiddle , 'middle'],
920
+ // [ editor.lang.image.alignTop , 'top']
921
+ ],
922
+ onChange: function() {
923
+ updatePreview( this.getDialog() );
924
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
925
+ },
926
+ setup: function( type, element ) {
927
+ if ( type == IMAGE ) {
928
+ var value = element.getStyle( 'float' );
929
+ switch ( value ) {
930
+ // Ignore those unrelated values.
931
+ case 'inherit':
932
+ case 'none':
933
+ value = '';
934
+ }
935
+
936
+ !value && ( value = ( element.getAttribute( 'align' ) || '' ).toLowerCase() );
937
+ this.setValue( value );
938
+ }
939
+ },
940
+ commit: function( type, element ) {
941
+ var value = this.getValue();
942
+ if ( type == IMAGE || type == PREVIEW ) {
943
+ if ( value )
944
+ element.setStyle( 'float', value );
945
+ else
946
+ element.removeStyle( 'float' );
947
+
948
+ if ( type == IMAGE ) {
949
+ value = ( element.getAttribute( 'align' ) || '' ).toLowerCase();
950
+ switch ( value ) {
951
+ // we should remove it only if it matches "left" or "right",
952
+ // otherwise leave it intact.
953
+ case 'left':
954
+ case 'right':
955
+ element.removeAttribute( 'align' );
956
+ }
957
+ }
958
+ } else if ( type == CLEANUP ) {
959
+ element.removeStyle( 'float' );
960
+ }
961
+ }
962
+ } ]
963
+ } ]
964
+ },
965
+ {
966
+ type: 'vbox',
967
+ height: '250px',
968
+ children: [ {
969
+ type: 'html',
970
+ id: 'htmlPreview',
971
+ style: 'width:95%;',
972
+ html: '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) + '<br>' +
973
+ '<div id="' + imagePreviewLoaderId + '" class="ImagePreviewLoader" style="display:none"><div class="loading">&nbsp;</div></div>' +
974
+ '<div class="ImagePreviewBox"><table><tr><td>' +
975
+ '<a href="javascript:void(0)" target="_blank" onclick="return false;" id="' + previewLinkId + '">' +
976
+ '<img id="' + previewImageId + '" alt="" /></a>' +
977
+ // jscs:disable maximumLineLength
978
+ ( editor.config.image_previewText || 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ' +
979
+ 'Maecenas feugiat consequat diam. Maecenas metus. Vivamus diam purus, cursus a, commodo non, facilisis vitae, ' +
980
+ 'nulla. Aenean dictum lacinia tortor. Nunc iaculis, nibh non iaculis aliquam, orci felis euismod neque, sed ornare massa mauris sed velit. Nulla pretium mi et risus. Fusce mi pede, tempor id, cursus ac, ullamcorper nec, enim. Sed tortor. Curabitur molestie. Duis velit augue, condimentum at, ultrices a, luctus ut, orci. Donec pellentesque egestas eros. Integer cursus, augue in cursus faucibus, eros pede bibendum sem, in tempus tellus justo quis ligula. Etiam eget tortor. Vestibulum rutrum, est ut placerat elementum, lectus nisl aliquam velit, tempor aliquam eros nunc nonummy metus. In eros metus, gravida a, gravida sed, lobortis id, turpis. Ut ultrices, ipsum at venenatis fringilla, sem nulla lacinia tellus, eget aliquet turpis mauris non enim. Nam turpis. Suspendisse lacinia. Curabitur ac tortor ut ipsum egestas elementum. Nunc imperdiet gravida mauris.' ) +
981
+ // jscs:enable maximumLineLength
982
+ '</td></tr></table></div></div>'
983
+ } ]
984
+ } ]
985
+ } ]
986
+ },
987
+ {
988
+ id: 'Link',
989
+ requiredContent: 'a[href]',
990
+ label: editor.lang.image.linkTab,
991
+ padding: 0,
992
+ elements: [ {
993
+ id: 'txtUrl',
994
+ type: 'text',
995
+ label: editor.lang.common.url,
996
+ style: 'width: 100%',
997
+ 'default': '',
998
+ setup: function( type, element ) {
999
+ if ( type == LINK ) {
1000
+ var href = element.data( 'cke-saved-href' );
1001
+ if ( !href )
1002
+ href = element.getAttribute( 'href' );
1003
+ this.setValue( href );
1004
+ }
1005
+ },
1006
+ commit: function( type, element ) {
1007
+ if ( type == LINK ) {
1008
+ if ( this.getValue() || this.isChanged() ) {
1009
+ var url = this.getValue();
1010
+ element.data( 'cke-saved-href', url );
1011
+ element.setAttribute( 'href', url );
1012
+
1013
+ if ( this.getValue() || !editor.config.image_removeLinkByEmptyURL )
1014
+ this.getDialog().addLink = true;
1015
+ else
1016
+ this.getDialog().addLink = false;
1017
+ }
1018
+ }
1019
+ }
1020
+ },
1021
+ {
1022
+ type: 'button',
1023
+ id: 'browse',
1024
+ className: 'cke_dialog_image_browse',
1025
+ filebrowser: {
1026
+ action: 'Browse',
1027
+ target: 'Link:txtUrl',
1028
+ url: editor.config.filebrowserImageBrowseLinkUrl
1029
+ },
1030
+ style: 'float:right',
1031
+ hidden: true,
1032
+ label: editor.lang.common.browseServer
1033
+ },
1034
+ {
1035
+ id: 'cmbTarget',
1036
+ type: 'select',
1037
+ requiredContent: 'a[target]',
1038
+ label: editor.lang.common.target,
1039
+ 'default': '',
1040
+ items: [
1041
+ [ editor.lang.common.notSet, '' ],
1042
+ [ editor.lang.common.targetNew, '_blank' ],
1043
+ [ editor.lang.common.targetTop, '_top' ],
1044
+ [ editor.lang.common.targetSelf, '_self' ],
1045
+ [ editor.lang.common.targetParent, '_parent' ]
1046
+ ],
1047
+ setup: function( type, element ) {
1048
+ if ( type == LINK )
1049
+ this.setValue( element.getAttribute( 'target' ) || '' );
1050
+ },
1051
+ commit: function( type, element ) {
1052
+ if ( type == LINK ) {
1053
+ if ( this.getValue() || this.isChanged() )
1054
+ element.setAttribute( 'target', this.getValue() );
1055
+ }
1056
+ }
1057
+ } ]
1058
+ },
1059
+ {
1060
+ id: 'Upload',
1061
+ hidden: true,
1062
+ filebrowser: 'uploadButton',
1063
+ label: editor.lang.image.upload,
1064
+ elements: [ {
1065
+ type: 'file',
1066
+ id: 'upload',
1067
+ label: editor.lang.image.btnUpload,
1068
+ style: 'height:40px',
1069
+ size: 38
1070
+ },
1071
+ {
1072
+ type: 'fileButton',
1073
+ id: 'uploadButton',
1074
+ filebrowser: 'info:txtUrl',
1075
+ label: editor.lang.image.btnUpload,
1076
+ 'for': [ 'Upload', 'upload' ]
1077
+ } ]
1078
+ },
1079
+ {
1080
+ id: 'advanced',
1081
+ label: editor.lang.common.advancedTab,
1082
+ elements: [ {
1083
+ type: 'hbox',
1084
+ widths: [ '50%', '25%', '25%' ],
1085
+ children: [ {
1086
+ type: 'text',
1087
+ id: 'linkId',
1088
+ requiredContent: 'img[id]',
1089
+ label: editor.lang.common.id,
1090
+ setup: function( type, element ) {
1091
+ if ( type == IMAGE )
1092
+ this.setValue( element.getAttribute( 'id' ) );
1093
+ },
1094
+ commit: function( type, element ) {
1095
+ if ( type == IMAGE ) {
1096
+ if ( this.getValue() || this.isChanged() )
1097
+ element.setAttribute( 'id', this.getValue() );
1098
+ }
1099
+ }
1100
+ },
1101
+ {
1102
+ id: 'cmbLangDir',
1103
+ type: 'select',
1104
+ requiredContent: 'img[dir]',
1105
+ style: 'width : 100px;',
1106
+ label: editor.lang.common.langDir,
1107
+ 'default': '',
1108
+ items: [
1109
+ [ editor.lang.common.notSet, '' ],
1110
+ [ editor.lang.common.langDirLtr, 'ltr' ],
1111
+ [ editor.lang.common.langDirRtl, 'rtl' ]
1112
+ ],
1113
+ setup: function( type, element ) {
1114
+ if ( type == IMAGE )
1115
+ this.setValue( element.getAttribute( 'dir' ) );
1116
+ },
1117
+ commit: function( type, element ) {
1118
+ if ( type == IMAGE ) {
1119
+ if ( this.getValue() || this.isChanged() )
1120
+ element.setAttribute( 'dir', this.getValue() );
1121
+ }
1122
+ }
1123
+ },
1124
+ {
1125
+ type: 'text',
1126
+ id: 'txtLangCode',
1127
+ requiredContent: 'img[lang]',
1128
+ label: editor.lang.common.langCode,
1129
+ 'default': '',
1130
+ setup: function( type, element ) {
1131
+ if ( type == IMAGE )
1132
+ this.setValue( element.getAttribute( 'lang' ) );
1133
+ },
1134
+ commit: function( type, element ) {
1135
+ if ( type == IMAGE ) {
1136
+ if ( this.getValue() || this.isChanged() )
1137
+ element.setAttribute( 'lang', this.getValue() );
1138
+ }
1139
+ }
1140
+ } ]
1141
+ },
1142
+ {
1143
+ type: 'text',
1144
+ id: 'txtGenLongDescr',
1145
+ requiredContent: 'img[longdesc]',
1146
+ label: editor.lang.common.longDescr,
1147
+ setup: function( type, element ) {
1148
+ if ( type == IMAGE )
1149
+ this.setValue( element.getAttribute( 'longDesc' ) );
1150
+ },
1151
+ commit: function( type, element ) {
1152
+ if ( type == IMAGE ) {
1153
+ if ( this.getValue() || this.isChanged() )
1154
+ element.setAttribute( 'longDesc', this.getValue() );
1155
+ }
1156
+ }
1157
+ },
1158
+ {
1159
+ type: 'hbox',
1160
+ widths: [ '50%', '50%' ],
1161
+ children: [ {
1162
+ type: 'text',
1163
+ id: 'txtGenClass',
1164
+ requiredContent: 'img(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
1165
+ label: editor.lang.common.cssClass,
1166
+ 'default': '',
1167
+ setup: function( type, element ) {
1168
+ if ( type == IMAGE )
1169
+ this.setValue( element.getAttribute( 'class' ) );
1170
+ },
1171
+ commit: function( type, element ) {
1172
+ if ( type == IMAGE ) {
1173
+ if ( this.getValue() || this.isChanged() )
1174
+ element.setAttribute( 'class', this.getValue() );
1175
+ }
1176
+ }
1177
+ },
1178
+ {
1179
+ type: 'text',
1180
+ id: 'txtGenTitle',
1181
+ requiredContent: 'img[title]',
1182
+ label: editor.lang.common.advisoryTitle,
1183
+ 'default': '',
1184
+ onChange: function() {
1185
+ updatePreview( this.getDialog() );
1186
+ },
1187
+ setup: function( type, element ) {
1188
+ if ( type == IMAGE )
1189
+ this.setValue( element.getAttribute( 'title' ) );
1190
+ },
1191
+ commit: function( type, element ) {
1192
+ if ( type == IMAGE ) {
1193
+ if ( this.getValue() || this.isChanged() )
1194
+ element.setAttribute( 'title', this.getValue() );
1195
+ } else if ( type == PREVIEW )
1196
+ element.setAttribute( 'title', this.getValue() );
1197
+ else if ( type == CLEANUP ) {
1198
+ element.removeAttribute( 'title' );
1199
+ }
1200
+ }
1201
+ } ]
1202
+ },
1203
+ {
1204
+ type: 'text',
1205
+ id: 'txtdlgGenStyle',
1206
+ requiredContent: 'img{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
1207
+ label: editor.lang.common.cssStyle,
1208
+ validate: CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
1209
+ 'default': '',
1210
+ setup: function( type, element ) {
1211
+ if ( type == IMAGE ) {
1212
+ var genStyle = element.getAttribute( 'style' );
1213
+ if ( !genStyle && element.$.style.cssText )
1214
+ genStyle = element.$.style.cssText;
1215
+ this.setValue( genStyle );
1216
+
1217
+ var height = element.$.style.height,
1218
+ width = element.$.style.width,
1219
+ aMatchH = ( height ? height : '' ).match( regexGetSize ),
1220
+ aMatchW = ( width ? width : '' ).match( regexGetSize );
1221
+
1222
+ this.attributesInStyle = {
1223
+ height: !!aMatchH,
1224
+ width: !!aMatchW
1225
+ };
1226
+ }
1227
+ },
1228
+ onChange: function() {
1229
+ commitInternally.call(
1230
+ this, [
1231
+ 'info:cmbFloat',
1232
+ 'info:cmbAlign',
1233
+ 'info:txtVSpace',
1234
+ 'info:txtHSpace',
1235
+ 'info:txtBorder',
1236
+ 'info:txtWidth',
1237
+ 'info:txtHeight'
1238
+ ]
1239
+ );
1240
+ updatePreview( this );
1241
+ },
1242
+ commit: function( type, element ) {
1243
+ if ( type == IMAGE && ( this.getValue() || this.isChanged() ) )
1244
+ element.setAttribute( 'style', this.getValue() );
1245
+
1246
+ }
1247
+ } ]
1248
+ } ]
1249
+ };
1250
+ };
1251
+
1252
+ CKEDITOR.dialog.add( 'image', function( editor ) {
1253
+ return imageDialog( editor, 'image' );
1254
+ } );
1255
+
1256
+ CKEDITOR.dialog.add( 'imagebutton', function( editor ) {
1257
+ return imageDialog( editor, 'imagebutton' );
1258
+ } );
1259
+ } )();