atrium 0.0.1.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. data/.gitignore +13 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +32 -0
  4. data/GETTING_STARTED.textile +109 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +320 -0
  7. data/Guardfile +10 -0
  8. data/README.mkd +56 -0
  9. data/Rakefile +6 -0
  10. data/app/assets/images/atrium/ajax-loader.gif +0 -0
  11. data/app/assets/images/atrium/lightbox-ico-loading.gif +0 -0
  12. data/app/assets/images/atrium/logo.png +0 -0
  13. data/app/assets/javascripts/atrium/atrium.js +281 -0
  14. data/app/assets/javascripts/chosen.jquery.js +899 -0
  15. data/app/assets/javascripts/ckeditor.warning.js +65 -0
  16. data/app/assets/javascripts/ckeditor/config.js.coffee +6 -0
  17. data/app/assets/javascripts/ckeditor/jquery.generateId.js +13 -0
  18. data/app/assets/javascripts/ckeditor/jquery.jeditable.ckeditor.js +59 -0
  19. data/app/assets/javascripts/ckeditor/plugins/linkItem/application_link.png +0 -0
  20. data/app/assets/javascripts/ckeditor/plugins/linkItem/dialogs/linkItem.js +1470 -0
  21. data/app/assets/javascripts/ckeditor/plugins/linkItem/plugin.js +16 -0
  22. data/app/assets/javascripts/ckeditor/plugins/linkItem/ui_toolbar_pencil.png +0 -0
  23. data/app/assets/javascripts/jquery.colorbox.js +864 -0
  24. data/app/assets/javascripts/jquery.jeditable.js +543 -0
  25. data/app/assets/stylesheets/atrium/atrium.css +216 -0
  26. data/app/assets/stylesheets/atrium/chosen-sprite.png +0 -0
  27. data/app/assets/stylesheets/atrium/chosen.css +367 -0
  28. data/app/assets/stylesheets/colorbox.css +42 -0
  29. data/app/assets/stylesheets/controls.png +0 -0
  30. data/app/assets/stylesheets/loading.gif +0 -0
  31. data/app/controllers/atrium/base_controller.rb +25 -0
  32. data/app/controllers/atrium/collection_exhibit_order_controller.rb +21 -0
  33. data/app/controllers/atrium/collections_controller.rb +165 -0
  34. data/app/controllers/atrium/customization_controller.rb +41 -0
  35. data/app/controllers/atrium/descriptions_controller.rb +177 -0
  36. data/app/controllers/atrium/exhibit_facet_order_controller.rb +19 -0
  37. data/app/controllers/atrium/exhibits_controller.rb +108 -0
  38. data/app/controllers/atrium/showcases_controller.rb +193 -0
  39. data/app/helpers/atrium/base_helper.rb +6 -0
  40. data/app/models/ability.rb +14 -0
  41. data/app/models/atrium/browse_level.rb +41 -0
  42. data/app/models/atrium/collection.rb +121 -0
  43. data/app/models/atrium/description.rb +128 -0
  44. data/app/models/atrium/essay.rb +21 -0
  45. data/app/models/atrium/exhibit.rb +58 -0
  46. data/app/models/atrium/search/facet.rb +29 -0
  47. data/app/models/atrium/search/facet_selection.rb +17 -0
  48. data/app/models/atrium/showcase.rb +173 -0
  49. data/app/models/atrium/showcase/facet_selection.rb +21 -0
  50. data/app/models/atrium/showcase/item.rb +19 -0
  51. data/app/models/role_mapper.rb +23 -0
  52. data/app/views/_user_util_links.html.erb +25 -0
  53. data/app/views/atrium/collections/_bookmark_control.html.erb +25 -0
  54. data/app/views/atrium/collections/_browse_index.html.erb +20 -0
  55. data/app/views/atrium/collections/_collection_items_index.html.erb +10 -0
  56. data/app/views/atrium/collections/_document.html.erb +24 -0
  57. data/app/views/atrium/collections/_document_list.html.erb +6 -0
  58. data/app/views/atrium/collections/_edit_navigation.html.erb +20 -0
  59. data/app/views/atrium/collections/_folder_control.html.erb +12 -0
  60. data/app/views/atrium/collections/_form.html.erb +93 -0
  61. data/app/views/atrium/collections/_listing.html.erb +18 -0
  62. data/app/views/atrium/collections/_navigation.html.erb +45 -0
  63. data/app/views/atrium/collections/_results_pagination.html.erb +9 -0
  64. data/app/views/atrium/collections/_sort_and_per_page.html.erb +23 -0
  65. data/app/views/atrium/collections/edit.html.erb +5 -0
  66. data/app/views/atrium/collections/new.html.erb +6 -0
  67. data/app/views/atrium/collections/show.html.erb +22 -0
  68. data/app/views/atrium/descriptions/_form.html.erb +64 -0
  69. data/app/views/atrium/descriptions/edit.html.erb +4 -0
  70. data/app/views/atrium/descriptions/index.html.erb +4 -0
  71. data/app/views/atrium/descriptions/new.html.erb +2 -0
  72. data/app/views/atrium/descriptions/show.html.erb +20 -0
  73. data/app/views/atrium/exhibits/_bookmark_control.html.erb +25 -0
  74. data/app/views/atrium/exhibits/_folder_control.html.erb +12 -0
  75. data/app/views/atrium/exhibits/_form.html.erb +108 -0
  76. data/app/views/atrium/exhibits/edit.html.erb +9 -0
  77. data/app/views/atrium/exhibits/new.html.erb +6 -0
  78. data/app/views/atrium/exhibits/show.html.erb +26 -0
  79. data/app/views/atrium/showcases/_showcase_navigation.html.erb +23 -0
  80. data/app/views/atrium/showcases/show.html.erb +1 -0
  81. data/app/views/catalog/_collection_home_text.html.erb +6 -0
  82. data/app/views/catalog/_constraints.html.erb +9 -0
  83. data/app/views/catalog/_default_collection_text.html.erb +18 -0
  84. data/app/views/catalog/_default_home_text.html.erb +6 -0
  85. data/app/views/catalog/_home_text.html.erb +6 -0
  86. data/app/views/catalog/_index_partials/_description.html.erb +8 -0
  87. data/app/views/catalog/_search_form.html.erb +66 -0
  88. data/app/views/catalog/_show_partials/_description.html.erb +15 -0
  89. data/app/views/catalog/browse_show.html.erb +18 -0
  90. data/app/views/catalog/index.html.erb +35 -0
  91. data/app/views/catalog/list_description.html.erb +24 -0
  92. data/app/views/catalog/list_item.html.erb +3 -0
  93. data/app/views/layouts/atrium.html.erb +86 -0
  94. data/app/views/layouts/atrium_themes/default.html.erb +4 -0
  95. data/app/views/layouts/item_listing.html.erb +36 -0
  96. data/app/views/listing/_document.html.erb +27 -0
  97. data/app/views/listing/_document_list.html.erb +3 -0
  98. data/app/views/listing/_item_search.html.erb +27 -0
  99. data/app/views/shared/_banner.html.erb +5 -0
  100. data/app/views/shared/_collection_complete_list.html.erb +33 -0
  101. data/app/views/shared/_collection_search_form.html.erb +12 -0
  102. data/app/views/shared/_collection_title_header.html.erb +3 -0
  103. data/app/views/shared/_featured_sources.html.erb +20 -0
  104. data/app/views/shared/_list_descriptions.html.erb +38 -0
  105. data/app/views/shared/_navigation_browse_levels.html.erb +34 -0
  106. data/app/views/shared/_showcase_configure_menu.html.erb +22 -0
  107. data/app/views/shared/_top_navigation.html.erb +9 -0
  108. data/atrium.gemspec +76 -0
  109. data/config/routes.rb +12 -0
  110. data/init.rb +3 -0
  111. data/lib/application_controller.rb +24 -0
  112. data/lib/atrium.rb +42 -0
  113. data/lib/atrium/application_helper.rb +24 -0
  114. data/lib/atrium/atrium_helper_behavior.rb +162 -0
  115. data/lib/atrium/catalog.rb +203 -0
  116. data/lib/atrium/collections_helper.rb +139 -0
  117. data/lib/atrium/controller.rb +10 -0
  118. data/lib/atrium/descriptions_helper.rb +23 -0
  119. data/lib/atrium/engine.rb +12 -0
  120. data/lib/atrium/layout_helper.rb +13 -0
  121. data/lib/atrium/routes.rb +99 -0
  122. data/lib/atrium/solr_helper.rb +440 -0
  123. data/lib/atrium/version.rb +3 -0
  124. data/lib/generators/atrium/assets_generator.rb +44 -0
  125. data/lib/generators/atrium/atrium_generator.rb +149 -0
  126. data/lib/generators/atrium/cucumber_support_generator.rb +29 -0
  127. data/lib/generators/atrium/templates/config/role_map_cucumber.yml +2 -0
  128. data/lib/generators/atrium/templates/config/role_map_development.yml +2 -0
  129. data/lib/generators/atrium/templates/config/role_map_production.yml +2 -0
  130. data/lib/generators/atrium/templates/config/role_map_test.yml +2 -0
  131. data/lib/generators/atrium/templates/config/solr.yml +10 -0
  132. data/lib/generators/atrium/templates/db/seeds.rb +87 -0
  133. data/lib/generators/atrium/templates/migrations/create_atrium_browse_levels.rb +18 -0
  134. data/lib/generators/atrium/templates/migrations/create_atrium_collections.rb +20 -0
  135. data/lib/generators/atrium/templates/migrations/create_atrium_descriptions.rb +16 -0
  136. data/lib/generators/atrium/templates/migrations/create_atrium_essays.rb +15 -0
  137. data/lib/generators/atrium/templates/migrations/create_atrium_exhibits.rb +16 -0
  138. data/lib/generators/atrium/templates/migrations/create_atrium_search_facets.rb +14 -0
  139. data/lib/generators/atrium/templates/migrations/create_atrium_showcase_facet_selections.rb +15 -0
  140. data/lib/generators/atrium/templates/migrations/create_atrium_showcase_items.rb +17 -0
  141. data/lib/generators/atrium/templates/migrations/create_atrium_showcases.rb +16 -0
  142. data/lib/generators/atrium/templates/themes/example.html.erb +21 -0
  143. data/lib/railties/all_tests.rake +23 -0
  144. data/lib/railties/atrium_cucumber.rake +121 -0
  145. data/lib/railties/index.rake +37 -0
  146. data/tasks/atrium.rake +268 -0
  147. data/tasks/atrium_rspec.rake +91 -0
  148. data/test_support/etc/Gemfile +33 -0
  149. data/test_support/etc/bundle_config +3 -0
  150. data/test_support/etc/rvmrc +32 -0
  151. data/test_support/features/atrium_collections.feature +162 -0
  152. data/test_support/features/atrium_exhibits.feature +164 -0
  153. data/test_support/features/step_definitions/atrium_collection_steps.rb +102 -0
  154. data/test_support/features/step_definitions/atrium_exhibit_steps.rb +53 -0
  155. data/test_support/features/step_definitions/bookmarks_steps.rb +6 -0
  156. data/test_support/features/step_definitions/error_steps.rb +5 -0
  157. data/test_support/features/step_definitions/folder_steps.rb +27 -0
  158. data/test_support/features/step_definitions/general_steps.rb +50 -0
  159. data/test_support/features/step_definitions/record_view_steps.rb +12 -0
  160. data/test_support/features/step_definitions/saved_searches_steps.rb +22 -0
  161. data/test_support/features/step_definitions/search_facets_steps.rb +29 -0
  162. data/test_support/features/step_definitions/search_history_steps.rb +9 -0
  163. data/test_support/features/step_definitions/search_result_steps.rb +114 -0
  164. data/test_support/features/step_definitions/search_steps.rb +103 -0
  165. data/test_support/features/step_definitions/user_steps.rb +5 -0
  166. data/test_support/features/step_definitions/web_steps.rb +213 -0
  167. data/test_support/features/support/env.rb +67 -0
  168. data/test_support/features/support/paths.rb +76 -0
  169. data/test_support/features/support/selectors.rb +40 -0
  170. data/test_support/fixtures/atrium_exhibits.yml +3 -0
  171. data/test_support/spec/controllers/atrium/base_controller_spec.rb +13 -0
  172. data/test_support/spec/controllers/atrium/collection_exhibit_order_controller_spec.rb +29 -0
  173. data/test_support/spec/controllers/atrium/collections_controller_spec.rb +185 -0
  174. data/test_support/spec/controllers/atrium/customization_controller_spec.rb +38 -0
  175. data/test_support/spec/controllers/atrium/descriptions_controller_spec.rb +150 -0
  176. data/test_support/spec/controllers/atrium/exhibit_facet_order_controller_spec.rb +36 -0
  177. data/test_support/spec/controllers/atrium/exhibits_controller_spec.rb +192 -0
  178. data/test_support/spec/controllers/atrium/showcases_controller_spec.rb +185 -0
  179. data/test_support/spec/helpers/atrium_collections_helper_spec.rb +147 -0
  180. data/test_support/spec/helpers/atrium_description_helper_spec.rb +47 -0
  181. data/test_support/spec/helpers/atrium_solr_helper_spec.rb +512 -0
  182. data/test_support/spec/models/atrium/browse_level_spec.rb +31 -0
  183. data/test_support/spec/models/atrium/collection_spec.rb +30 -0
  184. data/test_support/spec/models/atrium/description_spec.rb +67 -0
  185. data/test_support/spec/models/atrium/essay_spec.rb +12 -0
  186. data/test_support/spec/models/atrium/exhibit_spec.rb +76 -0
  187. data/test_support/spec/models/atrium/search/facet_selection_spec.rb +24 -0
  188. data/test_support/spec/models/atrium/search/facet_spec.rb +11 -0
  189. data/test_support/spec/models/atrium/search_facet_spec.rb +54 -0
  190. data/test_support/spec/models/atrium/showcase/facet_selection_spec.rb +15 -0
  191. data/test_support/spec/models/atrium/showcase/item_spec.rb +12 -0
  192. data/test_support/spec/models/atrium/showcase_facet_selection_spec.rb +65 -0
  193. data/test_support/spec/models/atrium/showcase_spec.rb +134 -0
  194. data/test_support/spec/rcov.opts +3 -0
  195. data/test_support/spec/routing/atrium/collections_routing_spec.rb +33 -0
  196. data/test_support/spec/spec.opts +4 -0
  197. data/test_support/spec/spec_helper.rb +47 -0
  198. data/test_support/spec/support/be_accessible_matcher.rb +8 -0
  199. data/test_support/spec/support/be_routed_mixin.rb +7 -0
  200. data/test_support/tmp/step_definitions/catalog_index_steps.rb +14 -0
  201. data/test_support/tmp/step_definitions/edit_document_steps.rb +67 -0
  202. data/test_support/tmp/step_definitions/hydra_metadata_steps.rb +3 -0
  203. data/test_support/tmp/step_definitions/hydra_steps.rb +8 -0
  204. data/test_support/tmp/step_definitions/search_steps.rb +86 -0
  205. data/test_support/tmp/step_definitions/searching_steps.rb +22 -0
  206. data/test_support/tmp/step_definitions/select_steps.rb +8 -0
  207. data/test_support/tmp/step_definitions/show_document_steps.rb +73 -0
  208. data/test_support/tmp/step_definitions/user_steps.rb +15 -0
  209. data/test_support/tmp/step_definitions/web_steps.rb +273 -0
  210. data/test_support/tmp/support/env.rb +54 -0
  211. data/test_support/tmp/support/paths.rb +57 -0
  212. metadata +1047 -0
@@ -0,0 +1,65 @@
1
+ /*
2
+ Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3
+ For licensing, see LICENSE.html or http://ckeditor.com/license
4
+ */
5
+
6
+ // This file is not required by CKEditor and may be safely ignored.
7
+ // It is just a helper file that displays a red message about browser compatibility
8
+ // at the top of the samples (if incompatible browser is detected).
9
+
10
+ if ( window.CKEDITOR )
11
+ {
12
+ (function()
13
+ {
14
+ var showCompatibilityMsg = function()
15
+ {
16
+ var env = CKEDITOR.env;
17
+
18
+ var html = '<p><strong>Your browser is not compatible with CKEditor.</strong>';
19
+
20
+ var browsers =
21
+ {
22
+ gecko : 'Firefox 2.0',
23
+ ie : 'Internet Explorer 6.0',
24
+ opera : 'Opera 9.5',
25
+ webkit : 'Safari 3.0'
26
+ };
27
+
28
+ var alsoBrowsers = '';
29
+
30
+ for ( var key in env )
31
+ {
32
+ if ( browsers[ key ] )
33
+ {
34
+ if ( env[key] )
35
+ html += ' CKEditor is compatible with ' + browsers[ key ] + ' or higher.';
36
+ else
37
+ alsoBrowsers += browsers[ key ] + '+, ';
38
+ }
39
+ }
40
+
41
+ alsoBrowsers = alsoBrowsers.replace( /\+,([^,]+), $/, '+ and $1' );
42
+
43
+ html += ' It is also compatible with ' + alsoBrowsers + '.';
44
+
45
+ html += '</p><p>With non compatible browsers, you should still be able to see and edit the contents (HTML) in a plain text field.</p>';
46
+
47
+ var alertsEl = document.getElementById( 'alerts' );
48
+ alertsEl && ( alertsEl.innerHTML = html );
49
+ };
50
+
51
+ var onload = function()
52
+ {
53
+ // Show a friendly compatibility message as soon as the page is loaded,
54
+ // for those browsers that are not compatible with CKEditor.
55
+ if ( !CKEDITOR.env.isCompatible )
56
+ showCompatibilityMsg();
57
+ };
58
+
59
+ // Register the onload listener.
60
+ if ( window.addEventListener )
61
+ window.addEventListener( 'load', onload, false );
62
+ else if ( window.attachEvent )
63
+ window.attachEvent( 'onload', onload );
64
+ })();
65
+ }
@@ -0,0 +1,6 @@
1
+ CKEDITOR.editorConfig = (config) ->
2
+ config.language = "fr"
3
+ config.uiColor = "#AADC6E"
4
+ config.width = "850"
5
+ config.height = "600"
6
+ true
@@ -0,0 +1,13 @@
1
+ ;(function($) {
2
+ $.generateId = function() {
3
+ return arguments.callee.prefix + arguments.callee.count++;
4
+ };
5
+ $.generateId.prefix = 'jq$';
6
+ $.generateId.count = 0;
7
+
8
+ $.fn.generateId = function() {
9
+ return this.each(function() {
10
+ this.id = $.generateId();
11
+ });
12
+ };
13
+ })(jQuery);
@@ -0,0 +1,59 @@
1
+ /*
2
+ * CKEditor input for Jeditable
3
+ *
4
+ * Adapted from Wysiwyg input for Jeditable by Mike Tuupola
5
+ * http://www.appelsiini.net/2008/9/wysiwyg-for-jeditable
6
+ *
7
+ * Copyright (c) 2009 Jeremy Bell
8
+ *
9
+ * Licensed under the MIT license:
10
+ * http://www.opensource.org/licenses/mit-license.php
11
+ *
12
+ * Depends on CKEditor:
13
+ * http://ckeditor/
14
+ *
15
+ * Revision: $Id$
16
+ *
17
+ */
18
+
19
+ (function($) {
20
+ $.editable.addInputType('ckeditor', {
21
+ /* Use default textarea instead of writing code here again. */
22
+ //element : $.editable.types.textarea.element,
23
+ element : function(settings, original) {
24
+
25
+ /* Hide textarea to avoid flicker. */
26
+ var textarea = $('<textarea>').css("opacity", "0").generateId();
27
+ if (settings.rows) {
28
+ textarea.attr('rows', settings.rows);
29
+ } else {
30
+ textarea.height(settings.height);
31
+ }
32
+ if (settings.cols) {
33
+ textarea.attr('cols', settings.cols);
34
+ } else {
35
+ textarea.width(settings.width);
36
+ }
37
+ $(this).append(textarea);
38
+ return(textarea);
39
+ },
40
+ content : function(string, settings, original) {
41
+ /* jWYSIWYG plugin uses .text() instead of .val() */
42
+ /* For some reason it did not work work with generated */
43
+ /* textareas so I am forcing the value here with .text() */
44
+ $('textarea', this).text(string);
45
+ },
46
+ plugin : function(settings, original) {
47
+ var self = this;
48
+ if (settings.ckeditor) {
49
+ setTimeout(function() { CKEDITOR.replace($('textarea', self).attr('id'), settings.ckeditor); }, 0);
50
+ } else {
51
+ setTimeout(function() { CKEDITOR.replace($('textarea', self).attr('id')); }, 0);
52
+ }
53
+ },
54
+ submit : function(settings, original) {
55
+ $('textarea', this).val(CKEDITOR.instances[$('textarea', this).attr('id')].getData());
56
+ CKEDITOR.instances[$('textarea', this).attr('id')].destroy();
57
+ }
58
+ });
59
+ })(jQuery);
@@ -0,0 +1,1470 @@
1
+ /*
2
+ Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3
+ For licensing, see LICENSE.html or http://ckeditor.com/license
4
+ */
5
+
6
+ CKEDITOR.dialog.add( 'linkItem', function( editor )
7
+ {
8
+ var plugin = CKEDITOR.plugins.link;
9
+ // Handles the event when the "Target" selection box is changed.
10
+ var targetChanged = function()
11
+ {
12
+ var dialog = this.getDialog(),
13
+ popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ),
14
+ targetName = dialog.getContentElement( 'target', 'linkTargetName' ),
15
+ value = this.getValue();
16
+
17
+ if ( !popupFeatures || !targetName )
18
+ return;
19
+
20
+ popupFeatures = popupFeatures.getElement();
21
+ popupFeatures.hide();
22
+ targetName.setValue( '' );
23
+
24
+ switch ( value )
25
+ {
26
+ case 'frame' :
27
+ targetName.setLabel( editor.lang.link.targetFrameName );
28
+ targetName.getElement().show();
29
+ break;
30
+ case 'popup' :
31
+ popupFeatures.show();
32
+ targetName.setLabel( editor.lang.link.targetPopupName );
33
+ targetName.getElement().show();
34
+ break;
35
+ default :
36
+ targetName.setValue( value );
37
+ targetName.getElement().hide();
38
+ break;
39
+ }
40
+
41
+ };
42
+
43
+ // Handles the event when the "Type" selection box is changed.
44
+ var linkTypeChanged = function()
45
+ {
46
+ var dialog = this.getDialog(),
47
+ partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
48
+ typeValue = this.getValue(),
49
+ uploadTab = dialog.definition.getContents( 'upload' ),
50
+ uploadInitiallyHidden = uploadTab && uploadTab.hidden;
51
+
52
+ if ( typeValue == 'url' )
53
+ {
54
+ if ( editor.config.linkShowTargetTab )
55
+ dialog.showPage( 'target' );
56
+ if ( !uploadInitiallyHidden )
57
+ dialog.showPage( 'upload' );
58
+ }
59
+ else
60
+ {
61
+ dialog.hidePage( 'target' );
62
+ if ( !uploadInitiallyHidden )
63
+ dialog.hidePage( 'upload' );
64
+ }
65
+
66
+ for ( var i = 0 ; i < partIds.length ; i++ )
67
+ {
68
+ var element = dialog.getContentElement( 'info', partIds[i] );
69
+ if ( !element )
70
+ continue;
71
+
72
+ element = element.getElement().getParent().getParent();
73
+ if ( partIds[i] == typeValue + 'Options' )
74
+ element.show();
75
+ else
76
+ element.hide();
77
+ }
78
+
79
+ dialog.layout();
80
+ };
81
+
82
+ // Loads the parameters in a selected link to the link dialog fields.
83
+ var javascriptProtocolRegex = /^javascript:/,
84
+ emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
85
+ emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,
86
+ emailBodyRegex = /body=([^;?:@&=$,\/]*)/,
87
+ anchorRegex = /^#(.*)$/,
88
+ urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
89
+ selectableTargets = /^(_(?:self|top|parent|blank))$/,
90
+ encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
91
+ functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/;
92
+
93
+ var popupRegex =
94
+ /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
95
+ var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
96
+
97
+ var parseLink = function( editor, element )
98
+ {
99
+ var href = ( element && ( element.data( 'cke-saved-href' ) || element.getAttribute( 'href' ) ) ) || '',
100
+ javascriptMatch,
101
+ emailMatch,
102
+ anchorMatch,
103
+ urlMatch,
104
+ display_text = ( element && ( element.data( 'cke-saved-display' ) || element.getAttribute( 'display' ) ) ) || '',
105
+ retval = {};
106
+
107
+ if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) )
108
+ {
109
+ if ( emailProtection == 'encode' )
110
+ {
111
+ href = href.replace( encodedEmailLinkRegex,
112
+ function ( match, protectedAddress, rest )
113
+ {
114
+ return 'mailto:' +
115
+ String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) +
116
+ ( rest && unescapeSingleQuote( rest ) );
117
+ });
118
+ }
119
+ // Protected email link as function call.
120
+ else if ( emailProtection )
121
+ {
122
+ href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs )
123
+ {
124
+ if ( funcName == compiledProtectionFunction.name )
125
+ {
126
+ retval.type = 'email';
127
+ var email = retval.email = {};
128
+
129
+ var paramRegex = /[^,\s]+/g,
130
+ paramQuoteRegex = /(^')|('$)/g,
131
+ paramsMatch = funcArgs.match( paramRegex ),
132
+ paramsMatchLength = paramsMatch.length,
133
+ paramName,
134
+ paramVal;
135
+
136
+ for ( var i = 0; i < paramsMatchLength; i++ )
137
+ {
138
+ paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) );
139
+ paramName = compiledProtectionFunction.params[ i ].toLowerCase();
140
+ email[ paramName ] = paramVal;
141
+ }
142
+ email.address = [ email.name, email.domain ].join( '@' );
143
+ }
144
+ } );
145
+ }
146
+ }
147
+
148
+ if ( !retval.type )
149
+ {
150
+ if ( ( anchorMatch = href.match( anchorRegex ) ) )
151
+ {
152
+ retval.type = 'anchor';
153
+ retval.anchor = {};
154
+ retval.anchor.name = retval.anchor.id = anchorMatch[1];
155
+ }
156
+ // Protected email link as encoded string.
157
+ else if ( ( emailMatch = href.match( emailRegex ) ) )
158
+ {
159
+ var subjectMatch = href.match( emailSubjectRegex ),
160
+ bodyMatch = href.match( emailBodyRegex );
161
+
162
+ retval.type = 'email';
163
+ var email = ( retval.email = {} );
164
+ email.address = emailMatch[ 1 ];
165
+ subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) );
166
+ bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) );
167
+ }
168
+ // urlRegex matches empty strings, so need to check for href as well.
169
+ else if ( href && ( urlMatch = href.match( urlRegex ) ) )
170
+ {
171
+ retval.type = 'url';
172
+ retval.url = {};
173
+ retval.url.protocol = urlMatch[1];
174
+ retval.url.url = urlMatch[2];
175
+ retval.disp = display_text;
176
+ }
177
+ else
178
+ retval.type = 'url';
179
+ }
180
+
181
+ // Load target and popup settings.
182
+ if ( element )
183
+ {
184
+ var target = element.getAttribute( 'target' );
185
+ retval.target = {};
186
+ retval.adv = {};
187
+
188
+ // IE BUG: target attribute is an empty string instead of null in IE if it's not set.
189
+ if ( !target )
190
+ {
191
+ var onclick = element.data( 'cke-pa-onclick' ) || element.getAttribute( 'onclick' ),
192
+ onclickMatch = onclick && onclick.match( popupRegex );
193
+ if ( onclickMatch )
194
+ {
195
+ retval.target.type = 'popup';
196
+ retval.target.name = onclickMatch[1];
197
+
198
+ var featureMatch;
199
+ while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) )
200
+ {
201
+ // Some values should remain numbers (#7300)
202
+ if ( ( featureMatch[2] == 'yes' || featureMatch[2] == '1' ) && !( featureMatch[1] in { height:1, width:1, top:1, left:1 } ) )
203
+ retval.target[ featureMatch[1] ] = true;
204
+ else if ( isFinite( featureMatch[2] ) )
205
+ retval.target[ featureMatch[1] ] = featureMatch[2];
206
+ }
207
+ }
208
+ }
209
+ else
210
+ {
211
+ var targetMatch = target.match( selectableTargets );
212
+ if ( targetMatch )
213
+ retval.target.type = retval.target.name = target;
214
+ else
215
+ {
216
+ retval.target.type = 'frame';
217
+ retval.target.name = target;
218
+ }
219
+ }
220
+
221
+ var me = this;
222
+ var advAttr = function( inputName, attrName )
223
+ {
224
+ var value = element.getAttribute( attrName );
225
+ if ( value !== null )
226
+ retval.adv[ inputName ] = value || '';
227
+ };
228
+ advAttr( 'advId', 'id' );
229
+ advAttr( 'advLangDir', 'dir' );
230
+ advAttr( 'advAccessKey', 'accessKey' );
231
+
232
+ retval.adv.advName =
233
+ element.data( 'cke-saved-name' )
234
+ || element.getAttribute( 'name' )
235
+ || '';
236
+ advAttr( 'advLangCode', 'lang' );
237
+ advAttr( 'advTabIndex', 'tabindex' );
238
+ advAttr( 'advTitle', 'title' );
239
+ advAttr( 'advContentType', 'type' );
240
+ CKEDITOR.plugins.link.synAnchorSelector ?
241
+ retval.adv.advCSSClasses = getLinkClass( element )
242
+ : advAttr( 'advCSSClasses', 'class' );
243
+ advAttr( 'advCharset', 'charset' );
244
+ advAttr( 'advStyles', 'style' );
245
+ advAttr( 'advRel', 'rel' );
246
+ }
247
+
248
+ // Find out whether we have any anchors in the editor.
249
+ var anchors = retval.anchors = [],
250
+ item;
251
+
252
+ // For some browsers we set contenteditable="false" on anchors, making document.anchors not to include them, so we must traverse the links manually (#7893).
253
+ if ( CKEDITOR.plugins.link.emptyAnchorFix )
254
+ {
255
+ var links = editor.document.getElementsByTag( 'a' );
256
+ for ( i = 0, count = links.count(); i < count; i++ )
257
+ {
258
+ item = links.getItem( i );
259
+ if ( item.data( 'cke-saved-name' ) || item.hasAttribute( 'name' ) )
260
+ anchors.push( { name : item.data( 'cke-saved-name' ) || item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) } );
261
+ }
262
+ }
263
+ else
264
+ {
265
+ var anchorList = new CKEDITOR.dom.nodeList( editor.document.$.anchors );
266
+ for ( var i = 0, count = anchorList.count(); i < count; i++ )
267
+ {
268
+ item = anchorList.getItem( i );
269
+ anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) };
270
+ }
271
+ }
272
+
273
+ if ( CKEDITOR.plugins.link.fakeAnchor )
274
+ {
275
+ var imgs = editor.document.getElementsByTag( 'img' );
276
+ for ( i = 0, count = imgs.count(); i < count; i++ )
277
+ {
278
+ if ( ( item = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, imgs.getItem( i ) ) ) )
279
+ anchors.push( { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) } );
280
+ }
281
+ }
282
+
283
+ // Record down the selected element in the dialog.
284
+ this._.selectedElement = element;
285
+ return retval;
286
+ };
287
+
288
+ var setupParams = function( page, data )
289
+ {
290
+ if ( data[page] )
291
+ this.setValue( data[page][this.id] || '' );
292
+ };
293
+
294
+ var setupPopupParams = function( data )
295
+ {
296
+ return setupParams.call( this, 'target', data );
297
+ };
298
+
299
+ var setupAdvParams = function( data )
300
+ {
301
+ return setupParams.call( this, 'adv', data );
302
+ };
303
+
304
+ var commitParams = function( page, data )
305
+ {
306
+ if ( !data[page] )
307
+ data[page] = {};
308
+
309
+ data[page][this.id] = this.getValue() || '';
310
+ };
311
+
312
+ var commitPopupParams = function( data )
313
+ {
314
+ return commitParams.call( this, 'target', data );
315
+ };
316
+
317
+ var commitAdvParams = function( data )
318
+ {
319
+ return commitParams.call( this, 'adv', data );
320
+ };
321
+
322
+ function unescapeSingleQuote( str )
323
+ {
324
+ return str.replace( /\\'/g, '\'' );
325
+ }
326
+
327
+ function escapeSingleQuote( str )
328
+ {
329
+ return str.replace( /'/g, '\\$&' );
330
+ }
331
+
332
+ var emailProtection = editor.config.emailProtection || '';
333
+
334
+ // Compile the protection function pattern.
335
+ if ( emailProtection && emailProtection != 'encode' )
336
+ {
337
+ var compiledProtectionFunction = {};
338
+
339
+ emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params )
340
+ {
341
+ compiledProtectionFunction.name = funcName;
342
+ compiledProtectionFunction.params = [];
343
+ params.replace( /[^,\s]+/g, function( param )
344
+ {
345
+ compiledProtectionFunction.params.push( param );
346
+ } );
347
+ } );
348
+ }
349
+
350
+ function protectEmailLinkAsFunction( email )
351
+ {
352
+ var retval,
353
+ name = compiledProtectionFunction.name,
354
+ params = compiledProtectionFunction.params,
355
+ paramName,
356
+ paramValue;
357
+
358
+ retval = [ name, '(' ];
359
+ for ( var i = 0; i < params.length; i++ )
360
+ {
361
+ paramName = params[ i ].toLowerCase();
362
+ paramValue = email[ paramName ];
363
+
364
+ i > 0 && retval.push( ',' );
365
+ retval.push( '\'',
366
+ paramValue ?
367
+ escapeSingleQuote( encodeURIComponent( email[ paramName ] ) )
368
+ : '',
369
+ '\'');
370
+ }
371
+ retval.push( ')' );
372
+ return retval.join( '' );
373
+ }
374
+
375
+ function protectEmailAddressAsEncodedString( address )
376
+ {
377
+ var charCode,
378
+ length = address.length,
379
+ encodedChars = [];
380
+ for ( var i = 0; i < length; i++ )
381
+ {
382
+ charCode = address.charCodeAt( i );
383
+ encodedChars.push( charCode );
384
+ }
385
+ return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')';
386
+ }
387
+
388
+ function getLinkClass( ele )
389
+ {
390
+ var className = ele.getAttribute( 'class' );
391
+ return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : '';
392
+ }
393
+
394
+ var commonLang = editor.lang.common,
395
+ linkLang = editor.lang.link;
396
+
397
+ return {
398
+ title : linkLang.title,
399
+ minWidth : 350,
400
+ minHeight : 230,
401
+ contents : [
402
+ {
403
+ id : 'info',
404
+ label : linkLang.info,
405
+ title : linkLang.info,
406
+ elements :
407
+ [
408
+ {
409
+ id : 'linkType',
410
+ type : 'select',
411
+ label : linkLang.type,
412
+ 'default' : 'url',
413
+ items :
414
+ [
415
+ [ linkLang.toUrl, 'url' ],
416
+ [ linkLang.toAnchor, 'anchor' ],
417
+ [ linkLang.toEmail, 'email' ]
418
+ ],
419
+ onChange : linkTypeChanged,
420
+ setup : function( data )
421
+ {
422
+ if ( data.type )
423
+ this.setValue( data.type );
424
+ },
425
+ commit : function( data )
426
+ {
427
+ data.type = this.getValue();
428
+ }
429
+ },
430
+ {
431
+ type : 'vbox',
432
+ id : 'urlOptions',
433
+ children :
434
+ [
435
+ {
436
+ type : 'hbox',
437
+ widths : [ '25%', '75%' ],
438
+ children :
439
+ [
440
+ {
441
+ id : 'protocol',
442
+ type : 'select',
443
+ label : commonLang.protocol,
444
+ 'default' : 'http://',
445
+ items :
446
+ [
447
+ // Force 'ltr' for protocol names in BIDI. (#5433)
448
+ [ 'http://\u200E', 'http://' ],
449
+ [ 'https://\u200E', 'https://' ],
450
+ [ 'ftp://\u200E', 'ftp://' ],
451
+ [ 'news://\u200E', 'news://' ],
452
+ [ linkLang.other , '' ]
453
+ ],
454
+ setup : function( data )
455
+ {
456
+ if ( data.url )
457
+ this.setValue( data.url.protocol || '' );
458
+ },
459
+ commit : function( data )
460
+ {
461
+ if ( !data.url )
462
+ data.url = {};
463
+
464
+ data.url.protocol = this.getValue();
465
+ }
466
+ },
467
+ {
468
+ type : 'text',
469
+ id : 'url',
470
+ label : commonLang.url,
471
+ required: true,
472
+ onLoad : function ()
473
+ {
474
+ this.allowOnChange = true;
475
+ },
476
+ onKeyUp : function()
477
+ {
478
+ this.allowOnChange = false;
479
+ var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),
480
+ url = this.getValue(),
481
+ urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/i,
482
+ urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/i;
483
+
484
+ var protocol = urlOnChangeProtocol.exec( url );
485
+ if ( protocol )
486
+ {
487
+ this.setValue( url.substr( protocol[ 0 ].length ) );
488
+ protocolCmb.setValue( protocol[ 0 ].toLowerCase() );
489
+ }
490
+ else if ( urlOnChangeTestOther.test( url ) )
491
+ protocolCmb.setValue( '' );
492
+
493
+ this.allowOnChange = true;
494
+ },
495
+ onChange : function()
496
+ {
497
+ if ( this.allowOnChange ) // Dont't call on dialog load.
498
+ this.onKeyUp();
499
+ },
500
+ validate : function()
501
+ {
502
+ var dialog = this.getDialog();
503
+
504
+ if ( dialog.getContentElement( 'info', 'linkType' ) &&
505
+ dialog.getValueOf( 'info', 'linkType' ) != 'url' )
506
+ return true;
507
+
508
+ if ( this.getDialog().fakeObj ) // Edit Anchor.
509
+ return true;
510
+
511
+ var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noUrl );
512
+ return func.apply( this );
513
+ },
514
+ setup : function( data )
515
+ {
516
+ this.allowOnChange = false;
517
+ if ( data.url )
518
+ this.setValue( data.url.url );
519
+ this.allowOnChange = true;
520
+
521
+ },
522
+ commit : function( data )
523
+ {
524
+ // IE will not trigger the onChange event if the mouse has been used
525
+ // to carry all the operations #4724
526
+ this.onChange();
527
+
528
+ if ( !data.url )
529
+ data.url = {};
530
+
531
+ data.url.url = this.getValue();
532
+ this.allowOnChange = false;
533
+ }
534
+ }
535
+ ],
536
+ setup : function( data )
537
+ {
538
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
539
+ this.getElement().show();
540
+ }
541
+ },
542
+ /*{
543
+ type : 'button',
544
+ id : 'browse',
545
+ hidden : 'true',
546
+ filebrowser : 'info:url',
547
+ label : commonLang.browseServer
548
+ }*/
549
+ {
550
+ type : 'hbox',
551
+ widths : [ '50%', '50%' ],
552
+ children :
553
+ [
554
+ {
555
+ type : 'button',
556
+ id : 'browse',
557
+ hidden : 'true',
558
+ filebrowser : 'info:url',
559
+ label : 'Link to File on Server'
560
+ },
561
+ {
562
+ type : 'button',
563
+ id : 'browseInternal',
564
+ label : 'Link to Item',
565
+ filebrowser :
566
+ {
567
+ action : 'Browse',
568
+ url : '/catalog'
569
+ }
570
+ }
571
+ ]
572
+ },
573
+ {
574
+ type : 'text',
575
+ label : 'Display',
576
+ id : 'displayField',
577
+ 'default' : 'Sample',
578
+ setup : function( data )
579
+ {
580
+ if ( data.disp )
581
+ this.setValue( data.disp );
582
+ },
583
+ commit : function( data )
584
+ {
585
+ data.disp = this.getValue();
586
+ }
587
+ }
588
+ ]
589
+ },
590
+ {
591
+ type : 'vbox',
592
+ id : 'anchorOptions',
593
+ width : 260,
594
+ align : 'center',
595
+ padding : 0,
596
+ children :
597
+ [
598
+ {
599
+ type : 'fieldset',
600
+ id : 'selectAnchorText',
601
+ label : linkLang.selectAnchor,
602
+ setup : function( data )
603
+ {
604
+ if ( data.anchors.length > 0 )
605
+ this.getElement().show();
606
+ else
607
+ this.getElement().hide();
608
+ },
609
+ children :
610
+ [
611
+ {
612
+ type : 'hbox',
613
+ id : 'selectAnchor',
614
+ children :
615
+ [
616
+ {
617
+ type : 'select',
618
+ id : 'anchorName',
619
+ 'default' : '',
620
+ label : linkLang.anchorName,
621
+ style : 'width: 100%;',
622
+ items :
623
+ [
624
+ [ '' ]
625
+ ],
626
+ setup : function( data )
627
+ {
628
+ this.clear();
629
+ this.add( '' );
630
+ for ( var i = 0 ; i < data.anchors.length ; i++ )
631
+ {
632
+ if ( data.anchors[i].name )
633
+ this.add( data.anchors[i].name );
634
+ }
635
+
636
+ if ( data.anchor )
637
+ this.setValue( data.anchor.name );
638
+
639
+ var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
640
+ if ( linkType && linkType.getValue() == 'email' )
641
+ this.focus();
642
+ },
643
+ commit : function( data )
644
+ {
645
+ if ( !data.anchor )
646
+ data.anchor = {};
647
+
648
+ data.anchor.name = this.getValue();
649
+ }
650
+ },
651
+ {
652
+ type : 'select',
653
+ id : 'anchorId',
654
+ 'default' : '',
655
+ label : linkLang.anchorId,
656
+ style : 'width: 100%;',
657
+ items :
658
+ [
659
+ [ '' ]
660
+ ],
661
+ setup : function( data )
662
+ {
663
+ this.clear();
664
+ this.add( '' );
665
+ for ( var i = 0 ; i < data.anchors.length ; i++ )
666
+ {
667
+ if ( data.anchors[i].id )
668
+ this.add( data.anchors[i].id );
669
+ }
670
+
671
+ if ( data.anchor )
672
+ this.setValue( data.anchor.id );
673
+ },
674
+ commit : function( data )
675
+ {
676
+ if ( !data.anchor )
677
+ data.anchor = {};
678
+
679
+ data.anchor.id = this.getValue();
680
+ }
681
+ }
682
+ ],
683
+ setup : function( data )
684
+ {
685
+ if ( data.anchors.length > 0 )
686
+ this.getElement().show();
687
+ else
688
+ this.getElement().hide();
689
+ }
690
+ }
691
+ ]
692
+ },
693
+ {
694
+ type : 'html',
695
+ id : 'noAnchors',
696
+ style : 'text-align: center;',
697
+ html : '<div role="label" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( linkLang.noAnchors ) + '</div>',
698
+ // Focus the first element defined in above html.
699
+ focus : true,
700
+ setup : function( data )
701
+ {
702
+ if ( data.anchors.length < 1 )
703
+ this.getElement().show();
704
+ else
705
+ this.getElement().hide();
706
+ }
707
+ }
708
+ ],
709
+ setup : function( data )
710
+ {
711
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
712
+ this.getElement().hide();
713
+ }
714
+ },
715
+ {
716
+ type : 'vbox',
717
+ id : 'emailOptions',
718
+ padding : 1,
719
+ children :
720
+ [
721
+ {
722
+ type : 'text',
723
+ id : 'emailAddress',
724
+ label : linkLang.emailAddress,
725
+ required : true,
726
+ validate : function()
727
+ {
728
+ var dialog = this.getDialog();
729
+
730
+ if ( !dialog.getContentElement( 'info', 'linkType' ) ||
731
+ dialog.getValueOf( 'info', 'linkType' ) != 'email' )
732
+ return true;
733
+
734
+ var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noEmail );
735
+ return func.apply( this );
736
+ },
737
+ setup : function( data )
738
+ {
739
+ if ( data.email )
740
+ this.setValue( data.email.address );
741
+
742
+ var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
743
+ if ( linkType && linkType.getValue() == 'email' )
744
+ this.select();
745
+ },
746
+ commit : function( data )
747
+ {
748
+ if ( !data.email )
749
+ data.email = {};
750
+
751
+ data.email.address = this.getValue();
752
+ }
753
+ },
754
+ {
755
+ type : 'text',
756
+ id : 'emailSubject',
757
+ label : linkLang.emailSubject,
758
+ setup : function( data )
759
+ {
760
+ if ( data.email )
761
+ this.setValue( data.email.subject );
762
+ },
763
+ commit : function( data )
764
+ {
765
+ if ( !data.email )
766
+ data.email = {};
767
+
768
+ data.email.subject = this.getValue();
769
+ }
770
+ },
771
+ {
772
+ type : 'textarea',
773
+ id : 'emailBody',
774
+ label : linkLang.emailBody,
775
+ rows : 3,
776
+ 'default' : '',
777
+ setup : function( data )
778
+ {
779
+ if ( data.email )
780
+ this.setValue( data.email.body );
781
+ },
782
+ commit : function( data )
783
+ {
784
+ if ( !data.email )
785
+ data.email = {};
786
+
787
+ data.email.body = this.getValue();
788
+ }
789
+ }
790
+ ],
791
+ setup : function( data )
792
+ {
793
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
794
+ this.getElement().hide();
795
+ }
796
+ }
797
+ ]
798
+ },
799
+ {
800
+ id : 'target',
801
+ label : linkLang.target,
802
+ title : linkLang.target,
803
+ elements :
804
+ [
805
+ {
806
+ type : 'hbox',
807
+ widths : [ '50%', '50%' ],
808
+ children :
809
+ [
810
+ {
811
+ type : 'select',
812
+ id : 'linkTargetType',
813
+ label : commonLang.target,
814
+ 'default' : 'notSet',
815
+ style : 'width : 100%;',
816
+ 'items' :
817
+ [
818
+ [ commonLang.notSet, 'notSet' ],
819
+ [ linkLang.targetFrame, 'frame' ],
820
+ [ linkLang.targetPopup, 'popup' ],
821
+ [ commonLang.targetNew, '_blank' ],
822
+ [ commonLang.targetTop, '_top' ],
823
+ [ commonLang.targetSelf, '_self' ],
824
+ [ commonLang.targetParent, '_parent' ]
825
+ ],
826
+ onChange : targetChanged,
827
+ setup : function( data )
828
+ {
829
+ if ( data.target )
830
+ this.setValue( data.target.type || 'notSet' );
831
+ targetChanged.call( this );
832
+ },
833
+ commit : function( data )
834
+ {
835
+ if ( !data.target )
836
+ data.target = {};
837
+
838
+ data.target.type = this.getValue();
839
+ }
840
+ },
841
+ {
842
+ type : 'text',
843
+ id : 'linkTargetName',
844
+ label : linkLang.targetFrameName,
845
+ 'default' : '',
846
+ setup : function( data )
847
+ {
848
+ if ( data.target )
849
+ this.setValue( data.target.name );
850
+ },
851
+ commit : function( data )
852
+ {
853
+ if ( !data.target )
854
+ data.target = {};
855
+
856
+ data.target.name = this.getValue().replace(/\W/gi, '');
857
+ }
858
+ }
859
+ ]
860
+ },
861
+ {
862
+ type : 'vbox',
863
+ width : '100%',
864
+ align : 'center',
865
+ padding : 2,
866
+ id : 'popupFeatures',
867
+ children :
868
+ [
869
+ {
870
+ type : 'fieldset',
871
+ label : linkLang.popupFeatures,
872
+ children :
873
+ [
874
+ {
875
+ type : 'hbox',
876
+ children :
877
+ [
878
+ {
879
+ type : 'checkbox',
880
+ id : 'resizable',
881
+ label : linkLang.popupResizable,
882
+ setup : setupPopupParams,
883
+ commit : commitPopupParams
884
+ },
885
+ {
886
+ type : 'checkbox',
887
+ id : 'status',
888
+ label : linkLang.popupStatusBar,
889
+ setup : setupPopupParams,
890
+ commit : commitPopupParams
891
+
892
+ }
893
+ ]
894
+ },
895
+ {
896
+ type : 'hbox',
897
+ children :
898
+ [
899
+ {
900
+ type : 'checkbox',
901
+ id : 'location',
902
+ label : linkLang.popupLocationBar,
903
+ setup : setupPopupParams,
904
+ commit : commitPopupParams
905
+
906
+ },
907
+ {
908
+ type : 'checkbox',
909
+ id : 'toolbar',
910
+ label : linkLang.popupToolbar,
911
+ setup : setupPopupParams,
912
+ commit : commitPopupParams
913
+
914
+ }
915
+ ]
916
+ },
917
+ {
918
+ type : 'hbox',
919
+ children :
920
+ [
921
+ {
922
+ type : 'checkbox',
923
+ id : 'menubar',
924
+ label : linkLang.popupMenuBar,
925
+ setup : setupPopupParams,
926
+ commit : commitPopupParams
927
+
928
+ },
929
+ {
930
+ type : 'checkbox',
931
+ id : 'fullscreen',
932
+ label : linkLang.popupFullScreen,
933
+ setup : setupPopupParams,
934
+ commit : commitPopupParams
935
+
936
+ }
937
+ ]
938
+ },
939
+ {
940
+ type : 'hbox',
941
+ children :
942
+ [
943
+ {
944
+ type : 'checkbox',
945
+ id : 'scrollbars',
946
+ label : linkLang.popupScrollBars,
947
+ setup : setupPopupParams,
948
+ commit : commitPopupParams
949
+
950
+ },
951
+ {
952
+ type : 'checkbox',
953
+ id : 'dependent',
954
+ label : linkLang.popupDependent,
955
+ setup : setupPopupParams,
956
+ commit : commitPopupParams
957
+
958
+ }
959
+ ]
960
+ },
961
+ {
962
+ type : 'hbox',
963
+ children :
964
+ [
965
+ {
966
+ type : 'text',
967
+ widths : [ '50%', '50%' ],
968
+ labelLayout : 'horizontal',
969
+ label : commonLang.width,
970
+ id : 'width',
971
+ setup : setupPopupParams,
972
+ commit : commitPopupParams
973
+
974
+ },
975
+ {
976
+ type : 'text',
977
+ labelLayout : 'horizontal',
978
+ widths : [ '50%', '50%' ],
979
+ label : linkLang.popupLeft,
980
+ id : 'left',
981
+ setup : setupPopupParams,
982
+ commit : commitPopupParams
983
+
984
+ }
985
+ ]
986
+ },
987
+ {
988
+ type : 'hbox',
989
+ children :
990
+ [
991
+ {
992
+ type : 'text',
993
+ labelLayout : 'horizontal',
994
+ widths : [ '50%', '50%' ],
995
+ label : commonLang.height,
996
+ id : 'height',
997
+ setup : setupPopupParams,
998
+ commit : commitPopupParams
999
+
1000
+ },
1001
+ {
1002
+ type : 'text',
1003
+ labelLayout : 'horizontal',
1004
+ label : linkLang.popupTop,
1005
+ widths : [ '50%', '50%' ],
1006
+ id : 'top',
1007
+ setup : setupPopupParams,
1008
+ commit : commitPopupParams
1009
+
1010
+ }
1011
+ ]
1012
+ }
1013
+ ]
1014
+ }
1015
+ ]
1016
+ }
1017
+ ]
1018
+ },
1019
+ {
1020
+ id : 'upload',
1021
+ label : linkLang.upload,
1022
+ title : linkLang.upload,
1023
+ hidden : true,
1024
+ filebrowser : 'uploadButton',
1025
+ elements :
1026
+ [
1027
+ {
1028
+ type : 'file',
1029
+ id : 'upload',
1030
+ label : commonLang.upload,
1031
+ style: 'height:40px',
1032
+ size : 29
1033
+ },
1034
+ {
1035
+ type : 'fileButton',
1036
+ id : 'uploadButton',
1037
+ label : commonLang.uploadSubmit,
1038
+ filebrowser : 'info:url',
1039
+ 'for' : [ 'upload', 'upload' ]
1040
+ }
1041
+ ]
1042
+ },
1043
+ {
1044
+ id : 'advanced',
1045
+ label : linkLang.advanced,
1046
+ title : linkLang.advanced,
1047
+ elements :
1048
+ [
1049
+ {
1050
+ type : 'vbox',
1051
+ padding : 1,
1052
+ children :
1053
+ [
1054
+ {
1055
+ type : 'hbox',
1056
+ widths : [ '45%', '35%', '20%' ],
1057
+ children :
1058
+ [
1059
+ {
1060
+ type : 'text',
1061
+ id : 'advId',
1062
+ label : linkLang.id,
1063
+ setup : setupAdvParams,
1064
+ commit : commitAdvParams
1065
+ },
1066
+ {
1067
+ type : 'select',
1068
+ id : 'advLangDir',
1069
+ label : linkLang.langDir,
1070
+ 'default' : '',
1071
+ style : 'width:110px',
1072
+ items :
1073
+ [
1074
+ [ commonLang.notSet, '' ],
1075
+ [ linkLang.langDirLTR, 'ltr' ],
1076
+ [ linkLang.langDirRTL, 'rtl' ]
1077
+ ],
1078
+ setup : setupAdvParams,
1079
+ commit : commitAdvParams
1080
+ },
1081
+ {
1082
+ type : 'text',
1083
+ id : 'advAccessKey',
1084
+ width : '80px',
1085
+ label : linkLang.acccessKey,
1086
+ maxLength : 1,
1087
+ setup : setupAdvParams,
1088
+ commit : commitAdvParams
1089
+
1090
+ }
1091
+ ]
1092
+ },
1093
+ {
1094
+ type : 'hbox',
1095
+ widths : [ '45%', '35%', '20%' ],
1096
+ children :
1097
+ [
1098
+ {
1099
+ type : 'text',
1100
+ label : linkLang.name,
1101
+ id : 'advName',
1102
+ setup : setupAdvParams,
1103
+ commit : commitAdvParams
1104
+
1105
+ },
1106
+ {
1107
+ type : 'text',
1108
+ label : linkLang.langCode,
1109
+ id : 'advLangCode',
1110
+ width : '110px',
1111
+ 'default' : '',
1112
+ setup : setupAdvParams,
1113
+ commit : commitAdvParams
1114
+
1115
+ },
1116
+ {
1117
+ type : 'text',
1118
+ label : linkLang.tabIndex,
1119
+ id : 'advTabIndex',
1120
+ width : '80px',
1121
+ maxLength : 5,
1122
+ setup : setupAdvParams,
1123
+ commit : commitAdvParams
1124
+
1125
+ }
1126
+ ]
1127
+ }
1128
+ ]
1129
+ },
1130
+ {
1131
+ type : 'vbox',
1132
+ padding : 1,
1133
+ children :
1134
+ [
1135
+ {
1136
+ type : 'hbox',
1137
+ widths : [ '45%', '55%' ],
1138
+ children :
1139
+ [
1140
+ {
1141
+ type : 'text',
1142
+ label : linkLang.advisoryTitle,
1143
+ 'default' : '',
1144
+ id : 'advTitle',
1145
+ setup : setupAdvParams,
1146
+ commit : commitAdvParams
1147
+
1148
+ },
1149
+ {
1150
+ type : 'text',
1151
+ label : linkLang.advisoryContentType,
1152
+ 'default' : '',
1153
+ id : 'advContentType',
1154
+ setup : setupAdvParams,
1155
+ commit : commitAdvParams
1156
+
1157
+ }
1158
+ ]
1159
+ },
1160
+ {
1161
+ type : 'hbox',
1162
+ widths : [ '45%', '55%' ],
1163
+ children :
1164
+ [
1165
+ {
1166
+ type : 'text',
1167
+ label : linkLang.cssClasses,
1168
+ 'default' : '',
1169
+ id : 'advCSSClasses',
1170
+ setup : setupAdvParams,
1171
+ commit : commitAdvParams
1172
+
1173
+ },
1174
+ {
1175
+ type : 'text',
1176
+ label : linkLang.charset,
1177
+ 'default' : '',
1178
+ id : 'advCharset',
1179
+ setup : setupAdvParams,
1180
+ commit : commitAdvParams
1181
+
1182
+ }
1183
+ ]
1184
+ },
1185
+ {
1186
+ type : 'hbox',
1187
+ widths : [ '45%', '55%' ],
1188
+ children :
1189
+ [
1190
+ {
1191
+ type : 'text',
1192
+ label : linkLang.rel,
1193
+ 'default' : '',
1194
+ id : 'advRel',
1195
+ setup : setupAdvParams,
1196
+ commit : commitAdvParams
1197
+ },
1198
+ {
1199
+ type : 'text',
1200
+ label : linkLang.styles,
1201
+ 'default' : '',
1202
+ id : 'advStyles',
1203
+ validate : CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
1204
+ setup : setupAdvParams,
1205
+ commit : commitAdvParams
1206
+ }
1207
+ ]
1208
+ }
1209
+ ]
1210
+ }
1211
+ ]
1212
+ }
1213
+ ],
1214
+ onShow : function()
1215
+ {
1216
+ var editor = this.getParentEditor(),
1217
+ selection = editor.getSelection(),
1218
+ element = null;
1219
+
1220
+ // Fill in all the relevant fields if there's already one link selected.
1221
+ if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
1222
+ selection.selectElement( element );
1223
+ else
1224
+ element = null;
1225
+
1226
+ this.setupContent( parseLink.apply( this, [ editor, element ] ) );
1227
+ },
1228
+ onOk : function()
1229
+ {
1230
+ var attributes = {},
1231
+ removeAttributes = [],
1232
+ data = {},
1233
+ me = this,
1234
+ editor = this.getParentEditor();
1235
+
1236
+ this.commitContent( data );
1237
+ // Compose the URL.
1238
+ switch ( data.type || 'url' )
1239
+ {
1240
+ case 'url':
1241
+ var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
1242
+ url = ( data.url && CKEDITOR.tools.trim( data.url.url ) ) || '';
1243
+ attributes[ 'data-cke-saved-href' ] = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url;
1244
+ var display = ( data.disp && data.disp!= undefined) ? data.disp : attributes[ 'data-cke-saved-href' ];
1245
+ attributes[ 'data-cke-saved-display' ] = display
1246
+ break;
1247
+ case 'anchor':
1248
+ var name = ( data.anchor && data.anchor.name ),
1249
+ id = ( data.anchor && data.anchor.id );
1250
+ attributes[ 'data-cke-saved-href' ] = '#' + ( name || id || '' );
1251
+ break;
1252
+ case 'email':
1253
+
1254
+ var linkHref,
1255
+ email = data.email,
1256
+ address = email.address;
1257
+
1258
+ switch( emailProtection )
1259
+ {
1260
+ case '' :
1261
+ case 'encode' :
1262
+ {
1263
+ var subject = encodeURIComponent( email.subject || '' ),
1264
+ body = encodeURIComponent( email.body || '' );
1265
+
1266
+ // Build the e-mail parameters first.
1267
+ var argList = [];
1268
+ subject && argList.push( 'subject=' + subject );
1269
+ body && argList.push( 'body=' + body );
1270
+ argList = argList.length ? '?' + argList.join( '&' ) : '';
1271
+
1272
+ if ( emailProtection == 'encode' )
1273
+ {
1274
+ linkHref = [ 'javascript:void(location.href=\'mailto:\'+',
1275
+ protectEmailAddressAsEncodedString( address ) ];
1276
+ // parameters are optional.
1277
+ argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' );
1278
+
1279
+ linkHref.push( ')' );
1280
+ }
1281
+ else
1282
+ linkHref = [ 'mailto:', address, argList ];
1283
+
1284
+ break;
1285
+ }
1286
+ default :
1287
+ {
1288
+ // Separating name and domain.
1289
+ var nameAndDomain = address.split( '@', 2 );
1290
+ email.name = nameAndDomain[ 0 ];
1291
+ email.domain = nameAndDomain[ 1 ];
1292
+
1293
+ linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ];
1294
+ }
1295
+ }
1296
+
1297
+ attributes[ 'data-cke-saved-href' ] = linkHref.join( '' );
1298
+ break;
1299
+ }
1300
+
1301
+ // Popups and target.
1302
+ if ( data.target )
1303
+ {
1304
+ if ( data.target.type == 'popup' )
1305
+ {
1306
+ var onclickList = [ 'window.open(this.href, \'',
1307
+ data.target.name || '', '\', \'' ];
1308
+ var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
1309
+ 'scrollbars', 'dependent' ];
1310
+ var featureLength = featureList.length;
1311
+ var addFeature = function( featureName )
1312
+ {
1313
+ if ( data.target[ featureName ] )
1314
+ featureList.push( featureName + '=' + data.target[ featureName ] );
1315
+ };
1316
+
1317
+ for ( var i = 0 ; i < featureLength ; i++ )
1318
+ featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ;
1319
+ addFeature( 'width' );
1320
+ addFeature( 'left' );
1321
+ addFeature( 'height' );
1322
+ addFeature( 'top' );
1323
+
1324
+ onclickList.push( featureList.join( ',' ), '\'); return false;' );
1325
+ attributes[ 'data-cke-pa-onclick' ] = onclickList.join( '' );
1326
+
1327
+ // Add the "target" attribute. (#5074)
1328
+ removeAttributes.push( 'target' );
1329
+ }
1330
+ else
1331
+ {
1332
+ if ( data.target.type != 'notSet' && data.target.name )
1333
+ attributes.target = data.target.name;
1334
+ else
1335
+ removeAttributes.push( 'target' );
1336
+
1337
+ removeAttributes.push( 'data-cke-pa-onclick', 'onclick' );
1338
+ }
1339
+ }
1340
+
1341
+ // Advanced attributes.
1342
+ if ( data.adv )
1343
+ {
1344
+ var advAttr = function( inputName, attrName )
1345
+ {
1346
+ var value = data.adv[ inputName ];
1347
+ if ( value )
1348
+ attributes[attrName] = value;
1349
+ else
1350
+ removeAttributes.push( attrName );
1351
+ };
1352
+
1353
+ advAttr( 'advId', 'id' );
1354
+ advAttr( 'advLangDir', 'dir' );
1355
+ advAttr( 'advAccessKey', 'accessKey' );
1356
+
1357
+ if ( data.adv[ 'advName' ] )
1358
+ attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ];
1359
+ else
1360
+ removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] );
1361
+
1362
+ advAttr( 'advLangCode', 'lang' );
1363
+ advAttr( 'advTabIndex', 'tabindex' );
1364
+ advAttr( 'advTitle', 'title' );
1365
+ advAttr( 'advContentType', 'type' );
1366
+ advAttr( 'advCSSClasses', 'class' );
1367
+ advAttr( 'advCharset', 'charset' );
1368
+ advAttr( 'advStyles', 'style' );
1369
+ advAttr( 'advRel', 'rel' );
1370
+ }
1371
+
1372
+
1373
+ // Browser need the "href" fro copy/paste link to work. (#6641)
1374
+ attributes.href = attributes[ 'data-cke-saved-href' ];
1375
+
1376
+ if ( !this._.selectedElement )
1377
+ {
1378
+ // Create element if current selection is collapsed.
1379
+ var selection = editor.getSelection(),
1380
+ ranges = selection.getRanges( true );
1381
+ if ( ranges.length == 1 && ranges[0].collapsed )
1382
+ {
1383
+ // Short mailto link text view (#5736).
1384
+ var text = new CKEDITOR.dom.text( data.type == 'email' ?
1385
+ data.email.address : attributes[ 'data-cke-saved-display' ], editor.document );
1386
+ ranges[0].insertNode( text );
1387
+ ranges[0].selectNodeContents( text );
1388
+ selection.selectRanges( ranges );
1389
+ }
1390
+
1391
+ // Apply style.
1392
+ var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
1393
+ style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
1394
+ style.apply( editor.document );
1395
+ }
1396
+ else
1397
+ {
1398
+ // We're only editing an existing link, so just overwrite the attributes.
1399
+ var element = this._.selectedElement,
1400
+ href = element.data( 'cke-saved-href' ),
1401
+ textView = element.getHtml();
1402
+
1403
+ element.setAttributes( attributes );
1404
+ element.removeAttributes( removeAttributes );
1405
+ if ( data.adv && data.adv.advName && CKEDITOR.plugins.link.synAnchorSelector )
1406
+ element.addClass( element.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' );
1407
+
1408
+ // Update text view when user changes protocol (#4612).
1409
+ if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 )
1410
+ {
1411
+ // Short mailto link text view (#5736).
1412
+ element.setHtml( data.type == 'email' ?
1413
+ data.email.address : attributes[ 'data-cke-saved-href' ] );
1414
+ }
1415
+ if ( attributes[ 'data-cke-saved-display' ] != textView )
1416
+ {
1417
+ // Change display text if changed
1418
+ element.setHtml(attributes[ 'data-cke-saved-display' ] );
1419
+ }
1420
+ delete this._.selectedElement;
1421
+ }
1422
+ },
1423
+ onLoad : function()
1424
+ {
1425
+ if ( !editor.config.linkShowAdvancedTab )
1426
+ this.hidePage( 'advanced' ); //Hide Advanded tab.
1427
+
1428
+ if ( !editor.config.linkShowTargetTab )
1429
+ this.hidePage( 'target' ); //Hide Target tab.
1430
+
1431
+ },
1432
+ // Inital focus on 'url' field if link is of type URL.
1433
+ onFocus : function()
1434
+ {
1435
+ var linkType = this.getContentElement( 'info', 'linkType' ),
1436
+ urlField;
1437
+ if ( linkType && linkType.getValue() == 'url' )
1438
+ {
1439
+ urlField = this.getContentElement( 'info', 'url' );
1440
+ urlField.select();
1441
+ }
1442
+ }
1443
+ };
1444
+ });
1445
+
1446
+ /**
1447
+ * The e-mail address anti-spam protection option. The protection will be
1448
+ * applied when creating or modifying e-mail links through the editor interface.<br>
1449
+ * Two methods of protection can be choosed:
1450
+ * <ol> <li>The e-mail parts (name, domain and any other query string) are
1451
+ * assembled into a function call pattern. Such function must be
1452
+ * provided by the developer in the pages that will use the contents.
1453
+ * <li>Only the e-mail address is obfuscated into a special string that
1454
+ * has no meaning for humans or spam bots, but which is properly
1455
+ * rendered and accepted by the browser.</li></ol>
1456
+ * Both approaches require JavaScript to be enabled.
1457
+ * @name CKEDITOR.config.emailProtection
1458
+ * @since 3.1
1459
+ * @type String
1460
+ * @default '' (empty string = disabled)
1461
+ * @example
1462
+ * // href="mailto:tester@ckeditor.com?subject=subject&body=body"
1463
+ * config.emailProtection = '';
1464
+ * @example
1465
+ * // href="<a href=\"javascript:void(location.href=\'mailto:\'+String.fromCharCode(116,101,115,116,101,114,64,99,107,101,100,105,116,111,114,46,99,111,109)+\'?subject=subject&body=body\')\">e-mail</a>"
1466
+ * config.emailProtection = 'encode';
1467
+ * @example
1468
+ * // href="javascript:mt('tester','ckeditor.com','subject','body')"
1469
+ * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';
1470
+ */