rich_cms 2.1.7 → 3.0.0

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 (179) hide show
  1. data/{CHANGELOG → CHANGELOG.rdoc} +24 -0
  2. data/README.textile +49 -33
  3. data/Rakefile +92 -10
  4. data/VERSION +1 -1
  5. data/app/controllers/rich/cms_controller.rb +10 -39
  6. data/app/controllers/rich/cms_sessions_controller.rb +31 -0
  7. data/app/views/rich/cms/dock/_menu.html.erb +10 -8
  8. data/app/views/rich/cms/dock/_panel.html.erb +1 -1
  9. data/app/views/rich/cms/dock/panel/edit/_rails2.html.erb +5 -3
  10. data/app/views/rich/cms/dock/panel/edit/_rails3.html.erb +4 -2
  11. data/app/views/rich/cms/dock/panel/login/_rails2.html.erb +18 -12
  12. data/app/views/rich/cms/dock/panel/login/_rails3.html.erb +18 -12
  13. data/app/views/rich_cms.html.erb +1 -1
  14. data/assets/jzip/jquery/cleditor.js +1132 -1132
  15. data/assets/jzip/jquery/extensions/browser_detect.js +1 -2
  16. data/assets/jzip/native/extensions.js +41 -0
  17. data/assets/jzip/rich/cms/editor.js +41 -7
  18. data/assets/jzip/rich_cms.jz +2 -1
  19. data/assets/sass/rich_cms/_panel.sass +11 -1
  20. data/config/routes.rb +12 -11
  21. data/lib/generators/rich/cms_admin/cms_admin_generator.rb +82 -0
  22. data/lib/generators/rich/{authlogic_user/templates → cms_admin/templates/authlogic}/migration.rb +0 -0
  23. data/lib/generators/rich/{authlogic_user/templates → cms_admin/templates/authlogic}/model.rb +0 -0
  24. data/lib/generators/rich/{authlogic_user/templates → cms_admin/templates/authlogic}/session.rb +0 -0
  25. data/lib/rich/cms/actionpack/action_controller/base.rb +3 -44
  26. data/lib/rich/cms/actionpack/action_view/base.rb +32 -10
  27. data/lib/rich/cms/actionpack.rb +1 -2
  28. data/lib/rich/cms/auth.rb +110 -0
  29. data/lib/rich/cms/content/group.rb +1 -2
  30. data/lib/rich/cms/content/item.rb +2 -3
  31. data/lib/rich/cms/core/string/html_safe.rb +1 -2
  32. data/lib/rich/cms/core/string.rb +1 -2
  33. data/lib/rich/cms/engine.rb +23 -51
  34. data/lib/rich/cms/rails/engine.rb +1 -2
  35. data/lib/rich/cms/version.rb +11 -0
  36. data/lib/rich_cms.rb +2 -2
  37. data/rails_generators/rich_cms_admin/lib/devise/route_devise.rb +32 -0
  38. data/rails_generators/rich_cms_admin/rich_cms_admin_generator.rb +113 -0
  39. data/rails_generators/{rich_authlogic_user/templates → rich_cms_admin/templates/authlogic}/migration.rb +0 -0
  40. data/rails_generators/{rich_authlogic_user/templates → rich_cms_admin/templates/authlogic}/model.rb +0 -0
  41. data/rails_generators/{rich_authlogic_user/templates → rich_cms_admin/templates/authlogic}/session.rb +0 -0
  42. data/rails_generators/{rich_authlogic_user → rich_cms_admin}/templates/config.rb +0 -0
  43. data/rails_generators/rich_cms_admin/templates/devise/README +23 -0
  44. data/rails_generators/rich_cms_admin/templates/devise/devise.rb +105 -0
  45. data/rails_generators/rich_cms_admin/templates/devise/en.yml +35 -0
  46. data/rails_generators/rich_cms_admin/templates/devise/migration.rb +23 -0
  47. data/rails_generators/rich_cms_admin/templates/devise/model.rb +9 -0
  48. data/rails_generators/rich_cms_content/rich_cms_content_generator.rb +18 -12
  49. data/rich_cms.gemspec +295 -105
  50. data/test/integrator.rb +89 -0
  51. data/test/rails-2/dummy/.bundle/config +2 -0
  52. data/test/rails-2/dummy/Gemfile +15 -0
  53. data/test/rails-2/dummy/Gemfile.lock +97 -0
  54. data/test/rails-2/dummy/Rakefile +10 -0
  55. data/test/rails-2/dummy/app/controllers/application_controller.rb +10 -0
  56. data/test/rails-2/dummy/app/helpers/application_helper.rb +3 -0
  57. data/test/rails-2/dummy/config/boot.rb +124 -0
  58. data/test/rails-2/dummy/config/database.yml +23 -0
  59. data/test/rails-2/dummy/config/environment.rb +41 -0
  60. data/test/rails-2/dummy/config/environments/development.rb +17 -0
  61. data/test/rails-2/dummy/config/environments/production.rb +28 -0
  62. data/test/rails-2/dummy/config/environments/test.rb +28 -0
  63. data/test/rails-2/dummy/config/initializers/backtrace_silencers.rb +7 -0
  64. data/test/rails-2/dummy/config/initializers/cookie_verification_secret.rb +7 -0
  65. data/test/rails-2/dummy/config/initializers/devise.rb +105 -0
  66. data/test/rails-2/dummy/config/initializers/enrichments.rb +5 -0
  67. data/test/rails-2/dummy/config/initializers/inflections.rb +10 -0
  68. data/test/rails-2/dummy/config/initializers/mime_types.rb +5 -0
  69. data/test/rails-2/dummy/config/initializers/new_rails_defaults.rb +21 -0
  70. data/test/rails-2/dummy/config/initializers/session_store.rb +15 -0
  71. data/test/rails-2/dummy/config/locales/en.yml +5 -0
  72. data/test/rails-2/dummy/config/preinitializer.rb +20 -0
  73. data/test/rails-2/dummy/config/routes.rb +44 -0
  74. data/test/rails-2/dummy/db/schema.rb +49 -0
  75. data/test/rails-2/dummy/db/seeds.rb +10 -0
  76. data/test/rails-2/dummy/public/404.html +30 -0
  77. data/test/rails-2/dummy/public/422.html +30 -0
  78. data/test/rails-2/dummy/public/500.html +30 -0
  79. data/test/rails-2/dummy/public/favicon.ico +0 -0
  80. data/test/rails-2/dummy/public/images/rails.png +0 -0
  81. data/test/rails-2/dummy/public/images/rich/cms/cleditor/buttons.gif +0 -0
  82. data/test/rails-2/dummy/public/images/rich/cms/cleditor/toolbar.gif +0 -0
  83. data/test/rails-2/dummy/public/javascripts/jquery/core.js +158 -0
  84. data/test/rails-2/dummy/public/javascripts/jquery/ui/rich_cms/core.js +1207 -0
  85. data/test/rails-2/dummy/public/javascripts/jquery/ui/rich_cms/draggable.js +798 -0
  86. data/test/rails-2/dummy/public/javascripts/jquery/ui/rich_cms/mouse.js +950 -0
  87. data/test/rails-2/dummy/public/javascripts/jquery/ui/rich_cms/widget.js +1188 -0
  88. data/test/rails-2/dummy/public/javascripts/rich_cms.js +1746 -0
  89. data/test/rails-2/dummy/public/robots.txt +5 -0
  90. data/test/rails-2/dummy/script/about +4 -0
  91. data/test/rails-2/dummy/script/console +3 -0
  92. data/test/rails-2/dummy/script/dbconsole +3 -0
  93. data/test/rails-2/dummy/script/destroy +3 -0
  94. data/test/rails-2/dummy/script/generate +3 -0
  95. data/test/rails-2/dummy/script/performance/benchmarker +3 -0
  96. data/test/rails-2/dummy/script/performance/profiler +3 -0
  97. data/test/rails-2/dummy/script/plugin +3 -0
  98. data/test/rails-2/dummy/script/runner +3 -0
  99. data/test/rails-2/dummy/script/server +3 -0
  100. data/test/rails-2/dummy/test/fixtures/authlogic_users.yml +6 -0
  101. data/test/rails-2/dummy/test/fixtures/devise_users.yml +5 -0
  102. data/test/rails-2/pending.rb +88 -0
  103. data/test/rails-2/test_helper.rb +13 -0
  104. data/test/rails-3/dummy/.bundle/config +2 -0
  105. data/test/rails-3/dummy/Gemfile +15 -0
  106. data/test/rails-3/dummy/Gemfile.lock +139 -0
  107. data/test/rails-3/dummy/Rakefile +7 -0
  108. data/test/rails-3/dummy/app/controllers/application_controller.rb +3 -0
  109. data/test/rails-3/dummy/app/helpers/application_helper.rb +2 -0
  110. data/test/rails-3/dummy/config/application.rb +42 -0
  111. data/test/rails-3/dummy/config/boot.rb +13 -0
  112. data/test/rails-3/dummy/config/database.yml +23 -0
  113. data/test/rails-3/dummy/config/environment.rb +5 -0
  114. data/test/rails-3/dummy/config/environments/development.rb +26 -0
  115. data/test/rails-3/dummy/config/environments/production.rb +49 -0
  116. data/test/rails-3/dummy/config/environments/test.rb +35 -0
  117. data/test/rails-3/dummy/config/initializers/backtrace_silencers.rb +7 -0
  118. data/test/rails-3/dummy/config/initializers/devise.rb +142 -0
  119. data/test/rails-3/dummy/config/initializers/enrichments.rb +5 -0
  120. data/test/rails-3/dummy/config/initializers/inflections.rb +10 -0
  121. data/test/rails-3/dummy/config/initializers/mime_types.rb +5 -0
  122. data/test/rails-3/dummy/config/initializers/secret_token.rb +7 -0
  123. data/test/rails-3/dummy/config/initializers/session_store.rb +8 -0
  124. data/test/rails-3/dummy/config/locales/en.yml +5 -0
  125. data/test/rails-3/dummy/config/routes.rb +59 -0
  126. data/test/rails-3/dummy/config.ru +4 -0
  127. data/test/rails-3/dummy/db/schema.rb +49 -0
  128. data/test/rails-3/dummy/db/seeds.rb +10 -0
  129. data/test/rails-3/dummy/public/404.html +26 -0
  130. data/test/rails-3/dummy/public/422.html +26 -0
  131. data/test/rails-3/dummy/public/500.html +26 -0
  132. data/test/rails-3/dummy/public/favicon.ico +0 -0
  133. data/test/rails-3/dummy/public/images/rich/cms/cleditor/buttons.gif +0 -0
  134. data/test/rails-3/dummy/public/images/rich/cms/cleditor/toolbar.gif +0 -0
  135. data/test/rails-3/dummy/public/javascripts/jquery/core.js +158 -0
  136. data/test/rails-3/dummy/public/javascripts/jquery/ui/rich_cms/core.js +1207 -0
  137. data/test/rails-3/dummy/public/javascripts/jquery/ui/rich_cms/draggable.js +798 -0
  138. data/test/rails-3/dummy/public/javascripts/jquery/ui/rich_cms/mouse.js +950 -0
  139. data/test/rails-3/dummy/public/javascripts/jquery/ui/rich_cms/widget.js +1188 -0
  140. data/test/rails-3/dummy/public/javascripts/rich_cms.js +1746 -0
  141. data/test/rails-3/dummy/script/rails +6 -0
  142. data/test/rails-3/dummy/test/fixtures/authlogic_users.yml +6 -0
  143. data/test/rails-3/dummy/test/fixtures/devise_users.yml +5 -0
  144. data/test/rails-3/test_helper.rb +17 -0
  145. data/test/shared/dummy/db/schema.rb +49 -0
  146. data/test/shared/dummy/db/seeds.rb +10 -0
  147. data/test/shared/dummy/fixtures/authlogic_users.yml +6 -0
  148. data/test/shared/dummy/models/authlogic_user.rb +8 -0
  149. data/test/shared/dummy/models/authlogic_user_session.rb +10 -0
  150. data/test/shared/dummy/models/cms_content.rb +2 -0
  151. data/test/shared/dummy/models/devise_user.rb +3 -0
  152. data/test/shared/dummy/stylesheets/app.css +100 -0
  153. data/test/shared/dummy/stylesheets/rich_cms.css +159 -0
  154. data/test/shared/dummy/views/application/index.html.erb +13 -0
  155. data/test/shared/dummy/views/layouts/application.html.erb +12 -0
  156. data/test/shared/support/action_controller/integration.rb +55 -0
  157. data/test/shared/support/action_controller/test_case.rb +28 -0
  158. data/test/shared/support/capybara/setup.rb +14 -0
  159. data/test/shared/support/test_helper.rb +1 -0
  160. data/test/shared/tests/actionpack/action_controller/base_test.rb +14 -0
  161. data/test/shared/tests/actionpack/action_view/base_test.rb +19 -0
  162. data/test/shared/tests/activesupport/active_support/dependencies_test.rb +15 -0
  163. data/test/shared/tests/app/integration/authenticated/authlogic.rb +88 -0
  164. data/test/shared/tests/app/integration/authenticated/devise_test.rb +89 -0
  165. data/test/shared/tests/app/integration/non_authenticated.rb +63 -0
  166. data/test/shared/tests/app/routing_test.rb +31 -0
  167. data/test/shared/tests/auth_test.rb +11 -0
  168. data/test/shared/tests/content/group.rb +10 -0
  169. data/test/shared/tests/content/item.rb +10 -0
  170. data/test/shared/tests/core/string/html_safe.rb +15 -0
  171. data/test/shared/tests/dummy_app.rb +228 -0
  172. data/test/shared/tests/engine_test.rb +19 -0
  173. data/test/shared/tests/rails/engine_test.rb +13 -0
  174. data/test/shared/tests/readme_test.rb +13 -0
  175. metadata +231 -63
  176. data/.gitignore +0 -2
  177. data/lib/generators/rich/authlogic_user/authlogic_user_generator.rb +0 -64
  178. data/rails_generators/rich_authlogic_user/rich_authlogic_user_generator.rb +0 -64
  179. data/test/test_helper.rb +0 -6
@@ -0,0 +1,1746 @@
1
+
2
+ var onRaccoonTipReady = function() {
3
+
4
+ /* Implement indexOf ourselves as IE does not support it */
5
+ if (!Array.prototype.indexOf)
6
+ {
7
+ Array.prototype.indexOf = function(searchElement /*, fromIndex */)
8
+ {
9
+ "use strict";
10
+
11
+ if (this === void 0 || this === null)
12
+ throw new TypeError();
13
+
14
+ var t = Object(this);
15
+ var len = t.length >>> 0;
16
+ if (len === 0)
17
+ return -1;
18
+
19
+ var n = 0;
20
+ if (arguments.length > 0)
21
+ {
22
+ n = Number(arguments[1]);
23
+ if (n !== n)
24
+ n = 0;
25
+ else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
26
+ n = (n > 0 || -1) * Math.floor(Math.abs(n));
27
+ }
28
+
29
+ if (n >= len)
30
+ return -1;
31
+
32
+ var k = n >= 0
33
+ ? n
34
+ : Math.max(len - Math.abs(n), 0);
35
+
36
+ for (; k < len; k++)
37
+ {
38
+ if (k in t && t[k] === searchElement)
39
+ return k;
40
+ }
41
+ return -1;
42
+ };
43
+ }
44
+
45
+ $.ajaxFormHandlers = {};
46
+
47
+ $.extend({
48
+ registerAjaxFormHandler: function(handlers) {
49
+ $.extend($.ajaxFormHandlers, handlers);
50
+ }
51
+ });
52
+
53
+ $("form.ajaxify").live("submit", function(event) {
54
+ var form = $(this);
55
+
56
+ $.ajax({
57
+ type: form.attr("method") || "GET",
58
+ url : form.attr("action") || window.location.href,
59
+ data: form.serialize(),
60
+ success: function(response) {
61
+ var handler = $.ajaxFormHandlers[form.attr("name")];
62
+ if (handler) {
63
+ handler(form, response);
64
+ }
65
+ }
66
+ });
67
+
68
+ event.preventDefault();
69
+ });
70
+
71
+ $.extend({
72
+ modules: function(object) {
73
+ var array = [];
74
+ $.each(object, function(property, names_only) {
75
+ if (property.match(/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ][abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+$/)) {
76
+ array.push(names_only === true ? property : object[property]);
77
+ }
78
+ });
79
+ return array;
80
+ },
81
+ initModules: function(namespace) {
82
+ $(function() {
83
+ $.each($.modules(namespace), function(i, module) {
84
+ if (module.init) {
85
+ module.init();
86
+ }
87
+ $.initModules(module);
88
+ });
89
+ });
90
+ }
91
+ });
92
+
93
+ $.extend({
94
+ keys: function(object) {
95
+ var result = [];
96
+ for (var key in object) {
97
+ if (object.hasOwnProperty(key)) {
98
+ result.push(key);
99
+ }
100
+ }
101
+ return result;
102
+ },
103
+ values: function(object) {
104
+ var result = [];
105
+ $.each($.keys(object), function(i, key) {
106
+ result.push(object[key]);
107
+ });
108
+ return result;
109
+ }
110
+ });
111
+
112
+ $.extend({
113
+ ie: jQuery.browser.msie,
114
+ ie6: jQuery.browser.msie && parseInt(jQuery.browser.version, 10) == 6,
115
+ ie7: jQuery.browser.msie && parseInt(jQuery.browser.version, 10) == 7,
116
+ ie8: jQuery.browser.msie && parseInt(jQuery.browser.version, 10) == 8,
117
+ ff2: jQuery.browser.mozilla && parseFloat(jQuery.browser.version) < 1.9
118
+ });
119
+
120
+ /*
121
+ @preserve CLEditor WYSIWYG HTML Editor v1.3.0
122
+ http://premiumsoftware.net/cleditor
123
+ requires jQuery v1.4.2 or later
124
+
125
+ Copyright 2010, Chris Landowski, Premium Software, LLC
126
+ Dual licensed under the MIT or GPL Version 2 licenses.
127
+ */
128
+
129
+ // ==ClosureCompiler==
130
+ // @compilation_level SIMPLE_OPTIMIZATIONS
131
+ // @output_file_name jquery.cleditor.min.js
132
+ // ==/ClosureCompiler==
133
+
134
+ (function($) {
135
+
136
+ //==============
137
+ // jQuery Plugin
138
+ //==============
139
+
140
+ $.cleditor = {
141
+
142
+ // Define the defaults used for all new cleditor instances
143
+ defaultOptions: {
144
+ width: 500, // width not including margins, borders or padding
145
+ height: 250, // height not including margins, borders or padding
146
+ controls: // controls to add to the toolbar
147
+ "bold italic underline strikethrough subscript superscript | font size " +
148
+ "style | color highlight removeformat | bullets numbering | outdent " +
149
+ "indent | alignleft center alignright justify | undo redo | " +
150
+ "rule image link unlink | cut copy paste pastetext | print source",
151
+ colors: // colors in the color popup
152
+ "FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF " +
153
+ "CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F " +
154
+ "BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C " +
155
+ "999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C " +
156
+ "666 900 C60 C93 990 090 399 33F 60C 939 " +
157
+ "333 600 930 963 660 060 366 009 339 636 " +
158
+ "000 300 630 633 330 030 033 006 309 303",
159
+ fonts: // font names in the font popup
160
+ "Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond," +
161
+ "Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",
162
+ sizes: // sizes in the font size popup
163
+ "1,2,3,4,5,6,7",
164
+ styles: // styles in the style popup
165
+ [["Paragraph", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"],
166
+ ["Header 3", "<h3>"], ["Header 4","<h4>"], ["Header 5","<h5>"],
167
+ ["Header 6","<h6>"]],
168
+ useCSS: false, // use CSS to style HTML when possible (not supported in ie)
169
+ docType: // Document type contained within the editor
170
+ '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
171
+ docCSSFile: // CSS file used to style the document contained within the editor
172
+ "",
173
+ bodyStyle: // style to assign to document body contained within the editor
174
+ "margin:4px; font:10pt Arial,Verdana; cursor:text"
175
+ },
176
+
177
+ // Define all usable toolbar buttons - the init string property is
178
+ // expanded during initialization back into the buttons object and
179
+ // seperate object properties are created for each button.
180
+ // e.g. buttons.size.title = "Font Size"
181
+ buttons: {
182
+ // name,title,command,popupName (""=use name)
183
+ init:
184
+ "bold,,|" +
185
+ "italic,,|" +
186
+ "underline,,|" +
187
+ "strikethrough,,|" +
188
+ "subscript,,|" +
189
+ "superscript,,|" +
190
+ "font,,fontname,|" +
191
+ "size,Font Size,fontsize,|" +
192
+ "style,,formatblock,|" +
193
+ "color,Font Color,forecolor,|" +
194
+ "highlight,Text Highlight Color,hilitecolor,color|" +
195
+ "removeformat,Remove Formatting,|" +
196
+ "bullets,,insertunorderedlist|" +
197
+ "numbering,,insertorderedlist|" +
198
+ "outdent,,|" +
199
+ "indent,,|" +
200
+ "alignleft,Align Text Left,justifyleft|" +
201
+ "center,,justifycenter|" +
202
+ "alignright,Align Text Right,justifyright|" +
203
+ "justify,,justifyfull|" +
204
+ "undo,,|" +
205
+ "redo,,|" +
206
+ "rule,Insert Horizontal Rule,inserthorizontalrule|" +
207
+ "image,Insert Image,insertimage,url|" +
208
+ "link,Insert Hyperlink,createlink,url|" +
209
+ "unlink,Remove Hyperlink,|" +
210
+ "cut,,|" +
211
+ "copy,,|" +
212
+ "paste,,|" +
213
+ "pastetext,Paste as Text,inserthtml,|" +
214
+ "print,,|" +
215
+ "source,Show Source"
216
+ },
217
+
218
+ // imagesPath - returns the path to the images folder
219
+ imagesPath: function() { return imagesPath(); }
220
+
221
+ };
222
+
223
+ // cleditor - creates a new editor for each of the matched textareas
224
+ $.fn.cleditor = function(options) {
225
+
226
+ // Create a new jQuery object to hold the results
227
+ var $result = $([]);
228
+
229
+ // Loop through all matching textareas and create the editors
230
+ this.each(function(idx, elem) {
231
+ if (elem.tagName == "TEXTAREA") {
232
+ var data = $.data(elem, CLEDITOR);
233
+ if (!data) data = new cleditor(elem, options);
234
+ $result = $result.add(data);
235
+ }
236
+ });
237
+
238
+ // return the new jQuery object
239
+ return $result;
240
+
241
+ };
242
+
243
+ //==================
244
+ // Private Variables
245
+ //==================
246
+
247
+ var
248
+
249
+ // Misc constants
250
+ BACKGROUND_COLOR = "backgroundColor",
251
+ BUTTON = "button",
252
+ BUTTON_NAME = "buttonName",
253
+ CHANGE = "change",
254
+ CLEDITOR = "cleditor",
255
+ CLICK = "click",
256
+ DISABLED = "disabled",
257
+ DIV_TAG = "<div>",
258
+ TRANSPARENT = "transparent",
259
+ UNSELECTABLE = "unselectable",
260
+
261
+ // Class name constants
262
+ MAIN_CLASS = "cleditorMain", // main containing div
263
+ TOOLBAR_CLASS = "cleditorToolbar", // toolbar div inside main div
264
+ GROUP_CLASS = "cleditorGroup", // group divs inside the toolbar div
265
+ BUTTON_CLASS = "cleditorButton", // button divs inside group div
266
+ DISABLED_CLASS = "cleditorDisabled",// disabled button divs
267
+ DIVIDER_CLASS = "cleditorDivider", // divider divs inside group div
268
+ POPUP_CLASS = "cleditorPopup", // popup divs inside body
269
+ LIST_CLASS = "cleditorList", // list popup divs inside body
270
+ COLOR_CLASS = "cleditorColor", // color popup div inside body
271
+ PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body
272
+ MSG_CLASS = "cleditorMsg", // message popup div inside body
273
+
274
+ // Test for ie
275
+ ie = $.browser.msie,
276
+ ie6 = /msie\s6/i.test(navigator.userAgent),
277
+
278
+ // Test for iPhone/iTouch/iPad
279
+ iOS = /iphone|ipad|ipod/i.test(navigator.userAgent),
280
+
281
+ // Popups are created once as needed and shared by all editor instances
282
+ popups = {},
283
+
284
+ // Used to prevent the document click event from being bound more than once
285
+ documentClickAssigned,
286
+
287
+ // Local copy of the buttons object
288
+ buttons = $.cleditor.buttons;
289
+
290
+ //===============
291
+ // Initialization
292
+ //===============
293
+
294
+ // Expand the buttons.init string back into the buttons object
295
+ // and create seperate object properties for each button.
296
+ // e.g. buttons.size.title = "Font Size"
297
+ $.each(buttons.init.split("|"), function(idx, button) {
298
+ var items = button.split(","), name = items[0];
299
+ buttons[name] = {
300
+ stripIndex: idx,
301
+ name: name,
302
+ title: items[1] === "" ? name.charAt(0).toUpperCase() + name.substr(1) : items[1],
303
+ command: items[2] === "" ? name : items[2],
304
+ popupName: items[3] === "" ? name : items[3]
305
+ };
306
+ });
307
+ delete buttons.init;
308
+
309
+ //============
310
+ // Constructor
311
+ //============
312
+
313
+ // cleditor - creates a new editor for the passed in textarea element
314
+ cleditor = function(area, options) {
315
+
316
+ var editor = this;
317
+
318
+ // Get the defaults and override with options
319
+ editor.options = options = $.extend({}, $.cleditor.defaultOptions, options);
320
+
321
+ // Hide the textarea and associate it with this editor
322
+ var $area = editor.$area = $(area)
323
+ .hide()
324
+ .data(CLEDITOR, editor)
325
+ .blur(function() {
326
+ // Update the iframe when the textarea loses focus
327
+ updateFrame(editor, true);
328
+ });
329
+
330
+ // Create the main container and append the textarea
331
+ var $main = editor.$main = $(DIV_TAG)
332
+ .addClass(MAIN_CLASS)
333
+ .width(options.width)
334
+ .height(options.height);
335
+
336
+ // Create the toolbar
337
+ var $toolbar = editor.$toolbar = $(DIV_TAG)
338
+ .addClass(TOOLBAR_CLASS)
339
+ .appendTo($main);
340
+
341
+ // Add the first group to the toolbar
342
+ var $group = $(DIV_TAG)
343
+ .addClass(GROUP_CLASS)
344
+ .appendTo($toolbar);
345
+
346
+ // Add the buttons to the toolbar
347
+ $.each(options.controls.split(" "), function(idx, buttonName) {
348
+ if (buttonName === "") return true;
349
+
350
+ // Divider
351
+ if (buttonName == "|") {
352
+
353
+ // Add a new divider to the group
354
+ var $div = $(DIV_TAG)
355
+ .addClass(DIVIDER_CLASS)
356
+ .appendTo($group);
357
+
358
+ // Create a new group
359
+ $group = $(DIV_TAG)
360
+ .addClass(GROUP_CLASS)
361
+ .appendTo($toolbar);
362
+
363
+ }
364
+
365
+ // Button
366
+ else {
367
+
368
+ // Get the button definition
369
+ var button = buttons[buttonName];
370
+
371
+ // Add a new button to the group
372
+ var $buttonDiv = $(DIV_TAG)
373
+ .data(BUTTON_NAME, button.name)
374
+ .addClass(BUTTON_CLASS)
375
+ .attr("title", button.title)
376
+ .bind(CLICK, $.proxy(buttonClick, editor))
377
+ .appendTo($group)
378
+ .hover(hoverEnter, hoverLeave);
379
+
380
+ // Prepare the button image
381
+ var map = {};
382
+ if (button.css) map = button.css;
383
+ else if (button.image) map.backgroundImage = imageUrl(button.image);
384
+ if (button.stripIndex) map.backgroundPosition = button.stripIndex * -24;
385
+ $buttonDiv.css(map);
386
+
387
+ // Add the unselectable attribute for ie
388
+ if (ie)
389
+ $buttonDiv.attr(UNSELECTABLE, "on");
390
+
391
+ // Create the popup
392
+ if (button.popupName)
393
+ createPopup(button.popupName, options, button.popupClass,
394
+ button.popupContent, button.popupHover);
395
+
396
+ }
397
+
398
+ });
399
+
400
+ // Add the main div to the DOM and append the textarea
401
+ $main.insertBefore($area)
402
+ .append($area);
403
+
404
+ // Bind the document click event handler
405
+ if (!documentClickAssigned) {
406
+ $(document).click(function(e) {
407
+ // Dismiss all non-prompt popups
408
+ var $target = $(e.target);
409
+ if (!$target.add($target.parents()).is("." + PROMPT_CLASS))
410
+ hidePopups();
411
+ });
412
+ documentClickAssigned = true;
413
+ }
414
+
415
+ // Bind the window resize event when the width or height is auto or %
416
+ if (/auto|%/.test("" + options.width + options.height))
417
+ $(window).resize(function() {refresh(editor);});
418
+
419
+ // Create the iframe and resize the controls
420
+ refresh(editor);
421
+
422
+ };
423
+
424
+ //===============
425
+ // Public Methods
426
+ //===============
427
+
428
+ var fn = cleditor.prototype,
429
+
430
+ // Expose the following private functions as methods on the cleditor object.
431
+ // The closure compiler will rename the private functions. However, the
432
+ // exposed method names on the cleditor object will remain fixed.
433
+ methods = [
434
+ ["clear", clear],
435
+ ["disable", disable],
436
+ ["execCommand", execCommand],
437
+ ["focus", focus],
438
+ ["hidePopups", hidePopups],
439
+ ["sourceMode", sourceMode, true],
440
+ ["refresh", refresh],
441
+ ["select", select],
442
+ ["selectedHTML", selectedHTML, true],
443
+ ["selectedText", selectedText, true],
444
+ ["showMessage", showMessage],
445
+ ["updateFrame", updateFrame],
446
+ ["updateTextArea", updateTextArea]
447
+ ];
448
+
449
+ $.each(methods, function(idx, method) {
450
+ fn[method[0]] = function() {
451
+ var editor = this, args = [editor];
452
+ // using each here would cast booleans into objects!
453
+ for(var x = 0; x < arguments.length; x++) {args.push(arguments[x]);}
454
+ var result = method[1].apply(editor, args);
455
+ if (method[2]) return result;
456
+ return editor;
457
+ };
458
+ });
459
+
460
+ // change - shortcut for .bind("change", handler) or .trigger("change")
461
+ fn.change = function(handler) {
462
+ var $this = $(this);
463
+ return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE);
464
+ };
465
+
466
+ //===============
467
+ // Event Handlers
468
+ //===============
469
+
470
+ // buttonClick - click event handler for toolbar buttons
471
+ function buttonClick(e) {
472
+
473
+ var editor = this,
474
+ buttonDiv = e.target,
475
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
476
+ button = buttons[buttonName],
477
+ popupName = button.popupName,
478
+ popup = popups[popupName];
479
+
480
+ // Check if disabled
481
+ if (editor.disabled || $(buttonDiv).attr(DISABLED) == DISABLED)
482
+ return;
483
+
484
+ // Fire the buttonClick event
485
+ var data = {
486
+ editor: editor,
487
+ button: buttonDiv,
488
+ buttonName: buttonName,
489
+ popup: popup,
490
+ popupName: popupName,
491
+ command: button.command,
492
+ useCSS: editor.options.useCSS
493
+ };
494
+
495
+ if (button.buttonClick && button.buttonClick(e, data) === false)
496
+ return false;
497
+
498
+ // Toggle source
499
+ if (buttonName == "source") {
500
+
501
+ // Show the iframe
502
+ if (sourceMode(editor)) {
503
+ delete editor.range;
504
+ editor.$area.hide();
505
+ editor.$frame.show();
506
+ buttonDiv.title = button.title;
507
+ }
508
+
509
+ // Show the textarea
510
+ else {
511
+ editor.$frame.hide();
512
+ editor.$area.show();
513
+ buttonDiv.title = "Show Rich Text";
514
+ }
515
+
516
+ // Enable or disable the toolbar buttons
517
+ // IE requires the timeout
518
+ setTimeout(function() {refreshButtons(editor);}, 100);
519
+
520
+ }
521
+
522
+ // Check for rich text mode
523
+ else if (!sourceMode(editor)) {
524
+
525
+ // Handle popups
526
+ if (popupName) {
527
+ var $popup = $(popup);
528
+
529
+ // URL
530
+ if (popupName == "url") {
531
+
532
+ // Check for selection before showing the link url popup
533
+ if (buttonName == "link" && selectedText(editor) === "") {
534
+ showMessage(editor, "A selection is required when inserting a link.", buttonDiv);
535
+ return false;
536
+ }
537
+
538
+ // Wire up the submit button click event handler
539
+ $popup.children(":button")
540
+ .unbind(CLICK)
541
+ .bind(CLICK, function() {
542
+
543
+ // Insert the image or link if a url was entered
544
+ var $text = $popup.find(":text"),
545
+ url = $.trim($text.val());
546
+ if (url !== "")
547
+ execCommand(editor, data.command, url, null, data.button);
548
+
549
+ // Reset the text, hide the popup and set focus
550
+ $text.val("http://");
551
+ hidePopups();
552
+ focus(editor);
553
+
554
+ });
555
+
556
+ }
557
+
558
+ // Paste as Text
559
+ else if (popupName == "pastetext") {
560
+
561
+ // Wire up the submit button click event handler
562
+ $popup.children(":button")
563
+ .unbind(CLICK)
564
+ .bind(CLICK, function() {
565
+
566
+ // Insert the unformatted text replacing new lines with break tags
567
+ var $textarea = $popup.find("textarea"),
568
+ text = $textarea.val().replace(/\n/g, "<br />");
569
+ if (text !== "")
570
+ execCommand(editor, data.command, text, null, data.button);
571
+
572
+ // Reset the text, hide the popup and set focus
573
+ $textarea.val("");
574
+ hidePopups();
575
+ focus(editor);
576
+
577
+ });
578
+
579
+ }
580
+
581
+ // Show the popup if not already showing for this button
582
+ if (buttonDiv !== $.data(popup, BUTTON)) {
583
+ showPopup(editor, popup, buttonDiv);
584
+ return false; // stop propagination to document click
585
+ }
586
+
587
+ // propaginate to documnt click
588
+ return;
589
+
590
+ }
591
+
592
+ // Print
593
+ else if (buttonName == "print")
594
+ editor.$frame[0].contentWindow.print();
595
+
596
+ // All other buttons
597
+ else if (!execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
598
+ return false;
599
+
600
+ }
601
+
602
+ // Focus the editor
603
+ focus(editor);
604
+
605
+ }
606
+
607
+ // hoverEnter - mouseenter event handler for buttons and popup items
608
+ function hoverEnter(e) {
609
+ var $div = $(e.target).closest("div");
610
+ $div.css(BACKGROUND_COLOR, $div.data(BUTTON_NAME) ? "#FFF" : "#FFC");
611
+ }
612
+
613
+ // hoverLeave - mouseleave event handler for buttons and popup items
614
+ function hoverLeave(e) {
615
+ $(e.target).closest("div").css(BACKGROUND_COLOR, "transparent");
616
+ }
617
+
618
+ // popupClick - click event handler for popup items
619
+ function popupClick(e) {
620
+
621
+ var editor = this,
622
+ popup = e.data.popup,
623
+ target = e.target;
624
+
625
+ // Check for message and prompt popups
626
+ if (popup === popups.msg || $(popup).hasClass(PROMPT_CLASS))
627
+ return;
628
+
629
+ // Get the button info
630
+ var buttonDiv = $.data(popup, BUTTON),
631
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
632
+ button = buttons[buttonName],
633
+ command = button.command,
634
+ value,
635
+ useCSS = editor.options.useCSS;
636
+
637
+ // Get the command value
638
+ if (buttonName == "font")
639
+ // Opera returns the fontfamily wrapped in quotes
640
+ value = target.style.fontFamily.replace(/"/g, "");
641
+ else if (buttonName == "size") {
642
+ if (target.tagName == "DIV")
643
+ target = target.children[0];
644
+ value = target.innerHTML;
645
+ }
646
+ else if (buttonName == "style")
647
+ value = "<" + target.tagName + ">";
648
+ else if (buttonName == "color")
649
+ value = hex(target.style.backgroundColor);
650
+ else if (buttonName == "highlight") {
651
+ value = hex(target.style.backgroundColor);
652
+ if (ie) command = 'backcolor';
653
+ else useCSS = true;
654
+ }
655
+
656
+ // Fire the popupClick event
657
+ var data = {
658
+ editor: editor,
659
+ button: buttonDiv,
660
+ buttonName: buttonName,
661
+ popup: popup,
662
+ popupName: button.popupName,
663
+ command: command,
664
+ value: value,
665
+ useCSS: useCSS
666
+ };
667
+
668
+ if (button.popupClick && button.popupClick(e, data) === false)
669
+ return;
670
+
671
+ // Execute the command
672
+ if (data.command && !execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
673
+ return false;
674
+
675
+ // Hide the popup and focus the editor
676
+ hidePopups();
677
+ focus(editor);
678
+
679
+ }
680
+
681
+ //==================
682
+ // Private Functions
683
+ //==================
684
+
685
+ // checksum - returns a checksum using the Adler-32 method
686
+ function checksum(text)
687
+ {
688
+ var a = 1, b = 0;
689
+ for (var index = 0; index < text.length; ++index) {
690
+ a = (a + text.charCodeAt(index)) % 65521;
691
+ b = (b + a) % 65521;
692
+ }
693
+ return (b << 16) | a;
694
+ }
695
+
696
+ // clear - clears the contents of the editor
697
+ function clear(editor) {
698
+ editor.$area.val("");
699
+ updateFrame(editor);
700
+ }
701
+
702
+ // createPopup - creates a popup and adds it to the body
703
+ function createPopup(popupName, options, popupTypeClass, popupContent, popupHover) {
704
+
705
+ // Check if popup already exists
706
+ if (popups[popupName])
707
+ return popups[popupName];
708
+
709
+ // Create the popup
710
+ var $popup = $(DIV_TAG)
711
+ .hide()
712
+ .addClass(POPUP_CLASS)
713
+ .appendTo("body");
714
+
715
+ // Add the content
716
+
717
+ // Custom popup
718
+ if (popupContent)
719
+ $popup.html(popupContent);
720
+
721
+ // Color
722
+ else if (popupName == "color") {
723
+ var colors = options.colors.split(" ");
724
+ if (colors.length < 10)
725
+ $popup.width("auto");
726
+ $.each(colors, function(idx, color) {
727
+ $(DIV_TAG).appendTo($popup)
728
+ .css(BACKGROUND_COLOR, "#" + color);
729
+ });
730
+ popupTypeClass = COLOR_CLASS;
731
+ }
732
+
733
+ // Font
734
+ else if (popupName == "font")
735
+ $.each(options.fonts.split(","), function(idx, font) {
736
+ $(DIV_TAG).appendTo($popup)
737
+ .css("fontFamily", font)
738
+ .html(font);
739
+ });
740
+
741
+ // Size
742
+ else if (popupName == "size")
743
+ $.each(options.sizes.split(","), function(idx, size) {
744
+ $(DIV_TAG).appendTo($popup)
745
+ .html("<font size=" + size + ">" + size + "</font>");
746
+ });
747
+
748
+ // Style
749
+ else if (popupName == "style")
750
+ $.each(options.styles, function(idx, style) {
751
+ $(DIV_TAG).appendTo($popup)
752
+ .html(style[1] + style[0] + style[1].replace("<", "</"));
753
+ });
754
+
755
+ // URL
756
+ else if (popupName == "url") {
757
+ $popup.html('Enter URL:<br><input type=text value="http://" size=35><br><input type=button value="Submit">');
758
+ popupTypeClass = PROMPT_CLASS;
759
+ }
760
+
761
+ // Paste as Text
762
+ else if (popupName == "pastetext") {
763
+ $popup.html('Paste your content here and click submit.<br /><textarea cols=40 rows=3></textarea><br /><input type=button value=Submit>');
764
+ popupTypeClass = PROMPT_CLASS;
765
+ }
766
+
767
+ // Add the popup type class name
768
+ if (!popupTypeClass && !popupContent)
769
+ popupTypeClass = LIST_CLASS;
770
+ $popup.addClass(popupTypeClass);
771
+
772
+ // Add the unselectable attribute to all items
773
+ if (ie) {
774
+ $popup.attr(UNSELECTABLE, "on")
775
+ .find("div,font,p,h1,h2,h3,h4,h5,h6")
776
+ .attr(UNSELECTABLE, "on");
777
+ }
778
+
779
+ // Add the hover effect to all items
780
+ if ($popup.hasClass(LIST_CLASS) || popupHover === true)
781
+ $popup.children().hover(hoverEnter, hoverLeave);
782
+
783
+ // Add the popup to the array and return it
784
+ popups[popupName] = $popup[0];
785
+ return $popup[0];
786
+
787
+ }
788
+
789
+ // disable - enables or disables the editor
790
+ function disable(editor, disabled) {
791
+
792
+ // Update the textarea and save the state
793
+ if (disabled) {
794
+ editor.$area.attr(DISABLED, DISABLED);
795
+ editor.disabled = true;
796
+ }
797
+ else {
798
+ editor.$area.removeAttr(DISABLED);
799
+ delete editor.disabled;
800
+ }
801
+
802
+ // Switch the iframe into design mode.
803
+ // ie6 does not support designMode.
804
+ // ie7 & ie8 do not properly support designMode="off".
805
+ try {
806
+ if (ie) editor.doc.body.contentEditable = !disabled;
807
+ else editor.doc.designMode = !disabled ? "on" : "off";
808
+ }
809
+ // Firefox 1.5 throws an exception that can be ignored
810
+ // when toggling designMode from off to on.
811
+ catch (err) {}
812
+
813
+ // Enable or disable the toolbar buttons
814
+ refreshButtons(editor);
815
+
816
+ }
817
+
818
+ // execCommand - executes a designMode command
819
+ function execCommand(editor, command, value, useCSS, button) {
820
+
821
+ // Restore the current ie selection
822
+ restoreRange(editor);
823
+
824
+ // Set the styling method
825
+ if (!ie) {
826
+ if (useCSS === undefined || useCSS === null)
827
+ useCSS = editor.options.useCSS;
828
+ editor.doc.execCommand("styleWithCSS", 0, useCSS.toString());
829
+ }
830
+
831
+ // Execute the command and check for error
832
+ var success = true, description;
833
+ if (ie && command.toLowerCase() == "inserthtml")
834
+ getRange(editor).pasteHTML(value);
835
+ else {
836
+ try { success = editor.doc.execCommand(command, 0, value || null); }
837
+ catch (err) { description = err.description; success = false; }
838
+ if (!success) {
839
+ if ("cutcopypaste".indexOf(command) > -1)
840
+ showMessage(editor, "For security reasons, your browser does not support the " +
841
+ command + " command. Try using the keyboard shortcut or context menu instead.",
842
+ button);
843
+ else
844
+ showMessage(editor,
845
+ (description ? description : "Error executing the " + command + " command."),
846
+ button);
847
+ }
848
+ }
849
+
850
+ // Enable the buttons
851
+ refreshButtons(editor);
852
+ return success;
853
+
854
+ }
855
+
856
+ // focus - sets focus to either the textarea or iframe
857
+ function focus(editor) {
858
+ setTimeout(function() {
859
+ if (sourceMode(editor)) editor.$area.focus();
860
+ else editor.$frame[0].contentWindow.focus();
861
+ refreshButtons(editor);
862
+ }, 0);
863
+ }
864
+
865
+ // getRange - gets the current text range object
866
+ function getRange(editor) {
867
+ if (ie) return getSelection(editor).createRange();
868
+ return getSelection(editor).getRangeAt(0);
869
+ }
870
+
871
+ // getSelection - gets the current text range object
872
+ function getSelection(editor) {
873
+ if (ie) return editor.doc.selection;
874
+ return editor.$frame[0].contentWindow.getSelection();
875
+ }
876
+
877
+ // Returns the hex value for the passed in string.
878
+ // hex("rgb(255, 0, 0)"); // #FF0000
879
+ // hex("#FF0000"); // #FF0000
880
+ // hex("#F00"); // #FF0000
881
+ function hex(s) {
882
+ var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s),
883
+ c = s.split("");
884
+ if (m) {
885
+ s = ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16);
886
+ while (s.length < 6)
887
+ s = "0" + s;
888
+ }
889
+ return "#" + (s.length == 6 ? s : c[1] + c[1] + c[2] + c[2] + c[3] + c[3]);
890
+ }
891
+
892
+ // hidePopups - hides all popups
893
+ function hidePopups() {
894
+ $.each(popups, function(idx, popup) {
895
+ $(popup)
896
+ .hide()
897
+ .unbind(CLICK)
898
+ .removeData(BUTTON);
899
+ });
900
+ }
901
+
902
+ // imagesPath - returns the path to the images folder
903
+ function imagesPath() {
904
+ var cssFile = "jquery.cleditor.css",
905
+ href = $("link[href$='" + cssFile +"']").attr("href");
906
+ return href.substr(0, href.length - cssFile.length) + "images/";
907
+ }
908
+
909
+ // imageUrl - Returns the css url string for a filemane
910
+ function imageUrl(filename) {
911
+ return "url(" + imagesPath() + filename + ")";
912
+ }
913
+
914
+ // refresh - creates the iframe and resizes the controls
915
+ function refresh(editor) {
916
+
917
+ var $main = editor.$main,
918
+ options = editor.options;
919
+
920
+ // Remove the old iframe
921
+ if (editor.$frame)
922
+ editor.$frame.remove();
923
+
924
+ // Create a new iframe
925
+ var $frame = editor.$frame = $('<iframe frameborder="0" src="javascript:true;">')
926
+ .hide()
927
+ .appendTo($main);
928
+
929
+ // Load the iframe document content
930
+ var contentWindow = $frame[0].contentWindow,
931
+ doc = editor.doc = contentWindow.document,
932
+ $doc = $(doc);
933
+
934
+ doc.open();
935
+ doc.write(
936
+ options.docType +
937
+ '<html>' +
938
+ ((options.docCSSFile === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCSSFile + '" /></head>') +
939
+ '<body style="' + options.bodyStyle + '"></body></html>'
940
+ );
941
+ doc.close();
942
+
943
+ // Work around for bug in IE which causes the editor to lose
944
+ // focus when clicking below the end of the document.
945
+ if (ie)
946
+ $doc.click(function() {focus(editor);});
947
+
948
+ // Load the content
949
+ updateFrame(editor);
950
+
951
+ // Bind the ie specific iframe event handlers
952
+ if (ie) {
953
+
954
+ // Save the current user selection. This code is needed since IE will
955
+ // reset the selection just after the beforedeactivate event and just
956
+ // before the beforeactivate event.
957
+ $doc.bind("beforedeactivate beforeactivate selectionchange keypress", function(e) {
958
+
959
+ // Flag the editor as inactive
960
+ if (e.type == "beforedeactivate")
961
+ editor.inactive = true;
962
+
963
+ // Get rid of the bogus selection and flag the editor as active
964
+ else if (e.type == "beforeactivate") {
965
+ if (!editor.inactive && editor.range && editor.range.length > 1)
966
+ editor.range.shift();
967
+ delete editor.inactive;
968
+ }
969
+
970
+ // Save the selection when the editor is active
971
+ else if (!editor.inactive) {
972
+ if (!editor.range)
973
+ editor.range = [];
974
+ editor.range.unshift(getRange(editor));
975
+
976
+ // We only need the last 2 selections
977
+ while (editor.range.length > 2)
978
+ editor.range.pop();
979
+ }
980
+
981
+ });
982
+
983
+ // Restore the text range when the iframe gains focus
984
+ $frame.focus(function() {
985
+ restoreRange(editor);
986
+ });
987
+
988
+ }
989
+
990
+ // Update the textarea when the iframe loses focus
991
+ ($.browser.mozilla ? $doc : $(contentWindow)).blur(function() {
992
+ updateTextArea(editor, true);
993
+ });
994
+
995
+ // Enable the toolbar buttons as the user types or clicks
996
+ $doc.click(hidePopups)
997
+ .bind("keyup mouseup", function() {
998
+ refreshButtons(editor);
999
+ });
1000
+
1001
+ // Show the textarea for iPhone/iTouch/iPad or
1002
+ // the iframe when design mode is supported.
1003
+ if (iOS) editor.$area.show();
1004
+ else $frame.show();
1005
+
1006
+ // Wait for the layout to finish - shortcut for $(document).ready()
1007
+ $(function() {
1008
+
1009
+ var $toolbar = editor.$toolbar,
1010
+ $group = $toolbar.children("div:last"),
1011
+ wid = $main.width();
1012
+
1013
+ // Resize the toolbar
1014
+ var hgt = $group.offset().top + $group.outerHeight() - $toolbar.offset().top + 1;
1015
+ $toolbar.height(hgt);
1016
+
1017
+ // Resize the iframe
1018
+ hgt = (/%/.test("" + options.height) ? $main.height() : parseInt(options.height)) - hgt;
1019
+ $frame.width(wid).height(hgt);
1020
+
1021
+ // Resize the textarea. IE6 textareas have a 1px top
1022
+ // & bottom margin that cannot be removed using css.
1023
+ editor.$area.width(wid).height(ie6 ? hgt - 2 : hgt);
1024
+
1025
+ // Switch the iframe into design mode if enabled
1026
+ disable(editor, editor.disabled);
1027
+
1028
+ // Enable or disable the toolbar buttons
1029
+ refreshButtons(editor);
1030
+
1031
+ });
1032
+
1033
+ }
1034
+
1035
+ // refreshButtons - enables or disables buttons based on availability
1036
+ function refreshButtons(editor) {
1037
+
1038
+ // Webkit requires focus before queryCommandEnabled will return anything but false
1039
+ if (!iOS && $.browser.webkit && !editor.focused) {
1040
+ editor.$frame[0].contentWindow.focus();
1041
+ window.focus();
1042
+ editor.focused = true;
1043
+ }
1044
+
1045
+ // Get the object used for checking queryCommandEnabled
1046
+ var queryObj = editor.doc;
1047
+ if (ie) queryObj = getRange(editor);
1048
+
1049
+ // Loop through each button
1050
+ var inSourceMode = sourceMode(editor);
1051
+ $.each(editor.$toolbar.find("." + BUTTON_CLASS), function(idx, elem) {
1052
+
1053
+ var $elem = $(elem),
1054
+ button = $.cleditor.buttons[$.data(elem, BUTTON_NAME)],
1055
+ command = button.command,
1056
+ enabled = true;
1057
+
1058
+ // Determine the state
1059
+ if (editor.disabled)
1060
+ enabled = false;
1061
+ else if (button.getEnabled) {
1062
+ var data = {
1063
+ editor: editor,
1064
+ button: elem,
1065
+ buttonName: button.name,
1066
+ popup: popups[button.popupName],
1067
+ popupName: button.popupName,
1068
+ command: button.command,
1069
+ useCSS: editor.options.useCSS
1070
+ };
1071
+ enabled = button.getEnabled(data);
1072
+ if (enabled === undefined)
1073
+ enabled = true;
1074
+ }
1075
+ else if (((inSourceMode || iOS) && button.name != "source") ||
1076
+ (ie && (command == "undo" || command == "redo")))
1077
+ enabled = false;
1078
+ else if (command && command != "print") {
1079
+ if (ie && command == "hilitecolor")
1080
+ command = "backcolor";
1081
+ // IE does not support inserthtml, so it's always enabled
1082
+ if (!ie || command != "inserthtml") {
1083
+ try {enabled = queryObj.queryCommandEnabled(command);}
1084
+ catch (err) {enabled = false;}
1085
+ }
1086
+ }
1087
+
1088
+ // Enable or disable the button
1089
+ if (enabled) {
1090
+ $elem.removeClass(DISABLED_CLASS);
1091
+ $elem.removeAttr(DISABLED);
1092
+ }
1093
+ else {
1094
+ $elem.addClass(DISABLED_CLASS);
1095
+ $elem.attr(DISABLED, DISABLED);
1096
+ }
1097
+
1098
+ });
1099
+ }
1100
+
1101
+ // restoreRange - restores the current ie selection
1102
+ function restoreRange(editor) {
1103
+ if (ie && editor.range)
1104
+ editor.range[0].select();
1105
+ }
1106
+
1107
+ // select - selects all the text in either the textarea or iframe
1108
+ function select(editor) {
1109
+ setTimeout(function() {
1110
+ if (sourceMode(editor)) editor.$area.select();
1111
+ else execCommand(editor, "selectall");
1112
+ }, 0);
1113
+ }
1114
+
1115
+ // selectedHTML - returns the current HTML selection or and empty string
1116
+ function selectedHTML(editor) {
1117
+ restoreRange(editor);
1118
+ var range = getRange(editor);
1119
+ if (ie)
1120
+ return range.htmlText;
1121
+ var layer = $("<layer>")[0];
1122
+ layer.appendChild(range.cloneContents());
1123
+ var html = layer.innerHTML;
1124
+ layer = null;
1125
+ return html;
1126
+ }
1127
+
1128
+ // selectedText - returns the current text selection or and empty string
1129
+ function selectedText(editor) {
1130
+ restoreRange(editor);
1131
+ if (ie) return getRange(editor).text;
1132
+ return getSelection(editor).toString();
1133
+ }
1134
+
1135
+ // showMessage - alert replacement
1136
+ function showMessage(editor, message, button) {
1137
+ var popup = createPopup("msg", editor.options, MSG_CLASS);
1138
+ popup.innerHTML = message;
1139
+ showPopup(editor, popup, button);
1140
+ }
1141
+
1142
+ // showPopup - shows a popup
1143
+ function showPopup(editor, popup, button) {
1144
+
1145
+ var offset, left, top, $popup = $(popup);
1146
+
1147
+ // Determine the popup location
1148
+ if (button) {
1149
+ var $button = $(button);
1150
+ offset = $button.offset();
1151
+ left = --offset.left;
1152
+ top = offset.top + $button.height();
1153
+ }
1154
+ else {
1155
+ var $toolbar = editor.$toolbar;
1156
+ offset = $toolbar.offset();
1157
+ left = Math.floor(($toolbar.width() - $popup.width()) / 2) + offset.left;
1158
+ top = offset.top + $toolbar.height() - 2;
1159
+ }
1160
+
1161
+ // Position and show the popup
1162
+ hidePopups();
1163
+ $popup.css({left: left, top: top})
1164
+ .show();
1165
+
1166
+ // Assign the popup button and click event handler
1167
+ if (button) {
1168
+ $.data(popup, BUTTON, button);
1169
+ $popup.bind(CLICK, {popup: popup}, $.proxy(popupClick, editor));
1170
+ }
1171
+
1172
+ // Focus the first input element if any
1173
+ setTimeout(function() {
1174
+ $popup.find(":text,textarea").eq(0).focus().select();
1175
+ }, 100);
1176
+
1177
+ }
1178
+
1179
+ // sourceMode - returns true if the textarea is showing
1180
+ function sourceMode(editor) {
1181
+ return editor.$area.is(":visible");
1182
+ }
1183
+
1184
+ // updateFrame - updates the iframe with the textarea contents
1185
+ function updateFrame(editor, checkForChange) {
1186
+
1187
+ var code = editor.$area.val(),
1188
+ options = editor.options,
1189
+ updateFrameCallback = options.updateFrame,
1190
+ $body = $(editor.doc.body);
1191
+
1192
+ // Check for textarea change to avoid unnecessary firing
1193
+ // of potentially heavy updateFrame callbacks.
1194
+ if (updateFrameCallback) {
1195
+ var sum = checksum(code);
1196
+ if (checkForChange && editor.areaChecksum == sum)
1197
+ return;
1198
+ editor.areaChecksum = sum;
1199
+ }
1200
+
1201
+ // Convert the textarea source code into iframe html
1202
+ var html = updateFrameCallback ? updateFrameCallback(code) : code;
1203
+
1204
+ // Prevent script injection attacks by html encoding script tags
1205
+ html = html.replace(/<(?=\/?script)/ig, "&lt;");
1206
+
1207
+ // Update the iframe checksum
1208
+ if (options.updateTextArea)
1209
+ editor.frameChecksum = checksum(html);
1210
+
1211
+ // Update the iframe and trigger the change event
1212
+ if (html != $body.html()) {
1213
+ $body.html(html);
1214
+ $(editor).triggerHandler(CHANGE);
1215
+ }
1216
+
1217
+ }
1218
+
1219
+ // updateTextArea - updates the textarea with the iframe contents
1220
+ function updateTextArea(editor, checkForChange) {
1221
+
1222
+ var html = $(editor.doc.body).html(),
1223
+ options = editor.options,
1224
+ updateTextAreaCallback = options.updateTextArea,
1225
+ $area = editor.$area;
1226
+
1227
+ // Check for iframe change to avoid unnecessary firing
1228
+ // of potentially heavy updateTextArea callbacks.
1229
+ if (updateTextAreaCallback) {
1230
+ var sum = checksum(html);
1231
+ if (checkForChange && editor.frameChecksum == sum)
1232
+ return;
1233
+ editor.frameChecksum = sum;
1234
+ }
1235
+
1236
+ // Convert the iframe html into textarea source code
1237
+ var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html;
1238
+
1239
+ // Update the textarea checksum
1240
+ if (options.updateFrame)
1241
+ editor.areaChecksum = checksum(code);
1242
+
1243
+ // Update the textarea and trigger the change event
1244
+ if (code != $area.val()) {
1245
+ $area.val(code);
1246
+ $(editor).triggerHandler(CHANGE);
1247
+ }
1248
+
1249
+ }
1250
+
1251
+ })(jQuery);
1252
+
1253
+ if (typeof(Rich) == "undefined") {
1254
+ Rich = {};
1255
+ $.initModules(Rich);
1256
+ }
1257
+
1258
+ Rich.Cms = {};
1259
+
1260
+ (function requireMissingLibs() {
1261
+ var id = "rc_dummy_script";
1262
+ document.write('<script id="' + id + '"></script>');
1263
+
1264
+ var dummy_script = document.getElementById(id);
1265
+ var element = dummy_script.previousSibling;
1266
+ while (element && (element.tagName.toLowerCase() != "script" || element.getAttribute("src").indexOf("rich_cms") == -1)) {
1267
+ element = element.previousSibling;
1268
+ }
1269
+ dummy_script.parentNode.removeChild(dummy_script);
1270
+
1271
+ var libs_file = ["core", "widget", "mouse", "draggable"][$.inArray("undefined", [typeof($.ui), typeof($.widget), typeof(($.ui || {}).mouse), typeof(($.ui || {}).draggable)])];
1272
+
1273
+ if (libs_file) {
1274
+ var src = element.getAttribute("src").replace(/(development\/)?(\w+)(\-min)?\.js.*$/, "jquery/ui/rich_cms/" + libs_file + ".js");
1275
+ document.write('<script src="' + src + '" type="text/javascript"></script>');
1276
+ }
1277
+ }());
1278
+
1279
+ Rich.Cms.Editor = (function() {
1280
+ var content_class = "rich_cms_content", mark_class = "marked", edit_panel = "#rich_cms_panel",
1281
+ editable_content = {}, content_items = "",
1282
+ cleditor_images_path = "/images/rich/cms/cleditor",
1283
+ cleditor_css = '<style>.cleditorMain {border:1px solid #999; padding:0 1px 1px; background-color:white} .cleditorMain iframe {border:none; margin:0; padding:0} .cleditorMain textarea {border:none; margin:0; padding:0; overflow-y:scroll; font:10pt Arial,Verdana; resize:none; outline:none /* webkit grip focus */} .cleditorToolbar {background: url("' + cleditor_images_path + '/toolbar.gif") repeat} .cleditorGroup {float:left; height:26px} .cleditorButton {float:left; width:24px; height:24px; margin:1px 0 1px 0; background: url("' + cleditor_images_path + '/buttons.gif")} .cleditorDisabled {opacity:0.3; filter:alpha(opacity=30)} .cleditorDivider {float:left; width:1px; height:23px; margin:1px 0 1px 0; background:#CCC} .cleditorPopup {border:solid 1px #999; background-color:white; position:absolute; font:10pt Arial,Verdana; cursor:default; z-index:10000} .cleditorList div {padding:2px 4px 2px 4px} .cleditorList p, .cleditorList h1, .cleditorList h2, .cleditorList h3, .cleditorList h4, .cleditorList h5, .cleditorList h6, .cleditorList font {padding:0; margin:0; background-color:Transparent} .cleditorColor {width:150px; padding:1px 0 0 1px} .cleditorColor div {float:left; width:14px; height:14px; margin:0 1px 1px 0} .cleditorPrompt {background-color:#F6F7F9; padding:4px; font-size:8.5pt} .cleditorPrompt input, .cleditorPrompt textarea {font:8.5pt Arial,Verdana;} .cleditorMsg {background-color:#FDFCEE; width:150px; padding:4px; font-size:8.5pt}</style>';
1284
+
1285
+ var register = function(hash) {
1286
+ $.extend(editable_content, hash);
1287
+ content_items = $.keys(editable_content).join(",");
1288
+ };
1289
+
1290
+ var bind = function() {
1291
+ $("#rich_cms_panel .edit form fieldset.inputs div.keys a.toggler").live("click", function(event) {
1292
+ event.preventDefault();
1293
+ var toggler = $(event.target);
1294
+ toggler.hide().closest(".keys").find("select[name=" + toggler.attr("data-name") + "]").show();
1295
+ });
1296
+
1297
+ $("#rich_cms_panel .edit a.close").bind("click", function(event) {
1298
+ event.preventDefault();
1299
+ RaccoonTip.close();
1300
+ });
1301
+
1302
+ RaccoonTip.register("." + content_class + "." + mark_class, "#rich_cms_panel", {
1303
+ beforeShow: edit,
1304
+ canHide: function() { return !$("#cleditor_input").length; },
1305
+ afterHide: function(content) { content.hide(); }
1306
+ });
1307
+
1308
+ bindSeatHolders();
1309
+ injectCleditorCss();
1310
+
1311
+ $.registerAjaxFormHandler({
1312
+ "rich_cms_content": afterUpdate
1313
+ });
1314
+ };
1315
+
1316
+ var bindSeatHolders = function() {
1317
+ RaccoonTip.register("." + content_class + "." + mark_class + ".sh_hint", "#rich_cms_panel", {event: "focus", beforeShow: edit, afterHide : function(content) { content.hide(); }});
1318
+ };
1319
+
1320
+ var injectCleditorCss = function() {
1321
+ if (!$("head").length) {
1322
+ $(document.body).before("<head></head>");
1323
+ }
1324
+ $(cleditor_css).prependTo("head");
1325
+ };
1326
+
1327
+ var mark = function(event) {
1328
+ event.preventDefault();
1329
+
1330
+ $(content_items).addClass(content_class).toggleClass(mark_class);
1331
+
1332
+ var markedContentItems = $(content_items + "." + mark_class);
1333
+ if (markedContentItems.length) {
1334
+ $.each(markedContentItems, function() {
1335
+ var item = $(this);
1336
+ if (item.find("p").length || item.html().length > 50) {
1337
+ item.addClass("block");
1338
+ }
1339
+ });
1340
+ bindSeatHolders();
1341
+ } else {
1342
+ $(content_items + ".block").removeClass("block");
1343
+ $(edit_panel).hide();
1344
+ }
1345
+
1346
+ if (typeof(SeatHolder) != "undefined") {
1347
+ SeatHolder.react(!markedContentItems.length);
1348
+ }
1349
+ };
1350
+
1351
+ var edit = function() {
1352
+ var content_item = $(this).closest(".rich_cms_content");
1353
+ var keys = $("#rich_cms_panel .edit form fieldset.inputs div.keys");
1354
+ var inputs = $("#rich_cms_panel .edit form fieldset.inputs");
1355
+
1356
+ var attrs = content_item.get(0).attributes;
1357
+ var selector = $.grep($.keys(editable_content), function(s) {
1358
+ return content_item.is(s);
1359
+ })[0];
1360
+ var specs = editable_content[selector];
1361
+
1362
+ keys.find("select,a,span").remove();
1363
+ inputs.find(":input,div.cleditorMain").remove();
1364
+ inputs.append("<input name='content_item[__selector__]' type='hidden' value='" + selector + "'/>");
1365
+
1366
+ $.each(attrs, function(index, attribute) {
1367
+ var attr = attribute.name;
1368
+
1369
+ if (attr.match(/^data-/)) {
1370
+ var name = "content_item[" + attr.replace(/^data-/, "") + "]";
1371
+ var value = content_item.attr(attr);
1372
+
1373
+ if (attr == specs.value) {
1374
+ var editable_input_type = content_item.attr("data-editable_input_type") || (content_item.is("textarea") || content_item.hasClass("block") ? "text" : "string");
1375
+
1376
+ switch (editable_input_type) {
1377
+ case "string":
1378
+ inputs.append("<input name='" + name + "' type='text' value='" + value + "'/>"); break;
1379
+ case "text":
1380
+ inputs.append("<textarea name='" + name + "'>" + value + "</textarea>"); break;
1381
+ case "html":
1382
+ inputs.append("<textarea id='cleditor_input' name='" + name + "' style='width: 500px; height: 300px'>" + value + "</textarea>"); break;
1383
+ }
1384
+ } else if (specs.keys.indexOf(attr) != -1) {
1385
+ var available_keys = $.map(value.split(","), function(key) { return $.trim(key); });
1386
+ var default_key = available_keys[0];
1387
+
1388
+ if (specs.keys.length > 1 && keys.find("select").length > 0) {
1389
+ keys.append("<span>, <span>");
1390
+ }
1391
+
1392
+ keys.append(available_keys.length == 1 ?
1393
+ "<span>" + default_key + "<span>" :
1394
+ "<a href='#' class='toggler' data-attr='" + attr + "' data-name='" + name + "'>" + default_key + "</a>");
1395
+ keys.append("<select name='" + name + "' style='display: none'>" +
1396
+ $.map(available_keys, function(key) { return "<option value='" + key + "'>" + key + "</option>"; }).join("") +
1397
+ "</select>");
1398
+ } else {
1399
+ inputs.append("<input name='" + name + "' type='hidden' value='" + value + "'/>");
1400
+ }
1401
+ }
1402
+ });
1403
+
1404
+ $("#rich_cms_panel .edit form fieldset.inputs div.keys select").bind("blur", function(event) {
1405
+ var select = $(event.target);
1406
+ var toggler = select.hide().closest(".keys").find(".toggler[data-name=" + select.attr("name") + "]").html(select.val()).show();
1407
+ var values = [select.val()];
1408
+
1409
+ $.map(select.find("option"), function(option) {
1410
+ var value = $(option).val();
1411
+ if (value != values[0]) {
1412
+ values.push(value);
1413
+ }
1414
+ });
1415
+
1416
+ content_item.attr(toggler.attr("data-attr"), values.join(", "));
1417
+ });
1418
+
1419
+ if (specs.beforeEdit) {
1420
+ var identifier = $.map(specs.keys, function(key) { return "[" + key + "=" + content_item.attr(key) + "]"; }).join("");
1421
+ specs.beforeEdit.apply(null, [inputs, selector, specs, identifier]);
1422
+ }
1423
+
1424
+ $(edit_panel).show();
1425
+
1426
+ setTimeout(function() {
1427
+ if ($("#cleditor_input").length) {
1428
+ $("#cleditor_input").data("cleditor", $("#cleditor_input").cleditor({
1429
+ width : 500,
1430
+ height: 300
1431
+ })[0].focus());
1432
+ }
1433
+ }, 250);
1434
+ };
1435
+
1436
+ var afterUpdate = function(form, response) {
1437
+ var selector = response["__selector__"];
1438
+ var specs = editable_content[selector];
1439
+ var identifier = $.map(specs.keys, function(key) { return "[" + key + "^=" + response["__identifier__"][key.replace(/^data-/, "")] + "]"; }).join("");
1440
+
1441
+ var defaultFunction = function(form, response, selector, specs, identifier) {
1442
+ $(identifier).html(response[specs.value.replace(/^data-/, "")]);
1443
+ if (typeof(SeatHolder) != "undefined") {
1444
+ SeatHolder.rebind();
1445
+ }
1446
+ };
1447
+
1448
+ (specs.afterUpdate || defaultFunction).apply(null, [form, response, selector, specs, identifier]);
1449
+ };
1450
+
1451
+ return {
1452
+ init: function() {
1453
+ bind();
1454
+ },
1455
+ register: register,
1456
+ mark: mark
1457
+ };
1458
+ }());
1459
+
1460
+ Rich.Cms.Dock = (function() {
1461
+ var bind = function() {
1462
+ if (!$.ie6) {
1463
+ makeDraggable();
1464
+ }
1465
+ };
1466
+
1467
+ var makeDraggable = function() {
1468
+ $("#rich_cms_dock").draggable({
1469
+ helper: "clone",
1470
+ handle: "#rich_cms_menu li:first",
1471
+ start: function(event, ui) {
1472
+ $("#x1,#x2,#y1,#x,#y").addClass("display");
1473
+ },
1474
+ drag: function(event, ui) {
1475
+ var x = event.pageX;
1476
+ var y = event.pageY;
1477
+ var x_div = $(window).width() / 3;
1478
+ var y_div = $(window).height() / 2;
1479
+
1480
+ $("#x" ).css({left: x });
1481
+ $("#y" ).css({top : y });
1482
+ $("#x1").css({left: x_div });
1483
+ $("#x2").css({left: x_div * 2});
1484
+ $("#y1").css({top : y_div });
1485
+
1486
+ if (x < x_div) {
1487
+ $("#rich_cms_dock") .addClass("left").removeClass("middle").removeClass("right");
1488
+ } else if (x > (x_div * 2)) {
1489
+ $("#rich_cms_dock").removeClass("left").removeClass("middle") .addClass("right");
1490
+ } else {
1491
+ $("#rich_cms_dock").removeClass("left"). addClass("middle").removeClass("right");
1492
+ }
1493
+
1494
+ if (y < y_div) {
1495
+ $("#rich_cms_dock") .addClass("top").removeClass("bottom");
1496
+ } else {
1497
+ $("#rich_cms_dock").removeClass("top").addClass("bottom");
1498
+ }
1499
+ },
1500
+ stop: function(event, ui) {
1501
+ $("#x1,#x2,#y1,#x,#y").removeClass("display");
1502
+ $.ajax({
1503
+ url: "/cms/position",
1504
+ data: {
1505
+ position: $.grep(($("#rich_cms_dock").attr("class") || "").split(" "), function(c) {
1506
+ return $.inArray(c, ["top", "bottom", "left", "middle", "right"]);
1507
+ }).join(" ")
1508
+ }
1509
+ });
1510
+ }
1511
+ });
1512
+ };
1513
+
1514
+ return {
1515
+ init: function() {
1516
+ bind();
1517
+ }
1518
+ };
1519
+ }());
1520
+
1521
+ Rich.Cms.Menu = (function() {
1522
+ var bind = function() {
1523
+ $("#rich_cms_menu a.mark").bind("click", Rich.Cms.Editor.mark);
1524
+ };
1525
+
1526
+ var register = function() {
1527
+ RaccoonTip.register("#rich_cms_menu a.login", "#rich_cms_panel",
1528
+ {beforeShow: function(content) { content.show(); },
1529
+ afterHide : function(content) { content.hide(); }});
1530
+ };
1531
+
1532
+ return {
1533
+ init: function() {
1534
+ bind();
1535
+ register();
1536
+ }
1537
+ };
1538
+ }());
1539
+
1540
+ };
1541
+
1542
+ if (typeof(RaccoonTip) == "undefined") {
1543
+
1544
+ // *
1545
+ // * RaccoonTip 1.0.8 (Uncompressed)
1546
+ // * A lightweight jQuery based balloon tip library
1547
+ // *
1548
+ // * This library requires jQuery (http://jquery.com)
1549
+ // *
1550
+ // * (c) 2010 Paul Engel (Internetbureau Holder B.V.)
1551
+ // * Except otherwise noted, RaccoonTip is licensed under
1552
+ // * http://creativecommons.org/licenses/by-sa/3.0
1553
+ // *
1554
+ // * $Date: 2010-10-17 13:37:39 +0100 (Sun, 17 October 2010) $
1555
+ // *
1556
+
1557
+ RaccoonTip = (function() {
1558
+ var html = '<div id="raccoon_tip" style="display: none"><div class="rt_tip"></div><div class="rt_content"></div></div>';
1559
+ var css = '<style>#raccoon_tip{*padding:14px;position:absolute;z-index:9999}#raccoon_tip .rt_tip{width:0;font-size:0;line-height:0;position:absolute;filter:chroma(color=pink)}#raccoon_tip.rt_bottom_right{margin-left:-28px;padding-top:14px}#raccoon_tip.rt_bottom_right .rt_tip{top:0;left:14px;border-bottom-width:14px;border-bottom-style:solid;border-bottom-color:#f9e98e;border-right-width:14px;border-right-style:solid;border-right-color:transparent;*border-right-color:pink}#raccoon_tip.rt_bottom_middle{padding-top:14px}#raccoon_tip.rt_bottom_middle .rt_tip{top:0;left:50%;margin-left:-7px;border-bottom-width:14px;border-bottom-style:solid;border-bottom-color:#f9e98e;border-left-width:7px;border-left-style:solid;border-left-color:transparent;*border-left-color:pink;border-right-width:7px;border-right-style:solid;border-right-color:transparent;*border-right-color:pink}#raccoon_tip.rt_bottom_left{margin-left:28px;padding-top:14px}#raccoon_tip.rt_bottom_left .rt_tip{top:0;right:14px;border-bottom-width:14px;border-bottom-style:solid;border-bottom-color:#f9e98e;border-left-width:14px;border-left-style:solid;border-left-color:transparent;*border-left-color:pink}#raccoon_tip.rt_middle_left{margin-left:-7px;padding-right:14px}#raccoon_tip.rt_middle_left .rt_tip{top:50%;right:0;margin-top:-7px;border-left-width:14px;border-left-style:solid;border-left-color:#f9e98e;border-top-width:7px;border-top-style:solid;border-top-color:transparent;*border-top-color:pink;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:transparent;*border-bottom-color:pink}#raccoon_tip.rt_top_left{margin-left:28px;padding-bottom:14px}#raccoon_tip.rt_top_left .rt_tip{bottom:0;right:14px;border-top-width:14px;border-top-style:solid;border-top-color:#f9e98e;border-left-width:14px;border-left-style:solid;border-left-color:transparent;*border-left-color:pink}#raccoon_tip.rt_top_middle{padding-bottom:14px}#raccoon_tip.rt_top_middle .rt_tip{bottom:0;left:50%;margin-left:-7px;border-top-width:14px;border-top-style:solid;border-top-color:#f9e98e;border-left-width:7px;border-left-style:solid;border-left-color:transparent;*border-left-color:pink;border-right-width:7px;border-right-style:solid;border-right-color:transparent;*border-right-color:pink}#raccoon_tip.rt_top_right{margin-left:-28px;padding-bottom:14px}#raccoon_tip.rt_top_right .rt_tip{bottom:0;left:14px;border-top-width:14px;border-top-style:solid;border-top-color:#f9e98e;border-right-width:14px;border-right-style:solid;border-right-color:transparent;*border-right-color:pink}#raccoon_tip.rt_middle_right{margin-left:7px;padding-left:14px}#raccoon_tip.rt_middle_right .rt_tip{top:50%;left:0;margin-top:-7px;border-right-width:14px;border-right-style:solid;border-right-color:#f9e98e;border-top-width:7px;border-top-style:solid;border-top-color:transparent;*border-top-color:pink;border-bottom-width:7px;border-bottom-style:solid;border-bottom-color:transparent;*border-bottom-color:pink}#raccoon_tip .rt_content{padding:6px 12px 8px 12px;overflow:hidden;background:#fbf7aa;border-width:10px;border-style:solid;border-color:#f9e98e;*border-width:7px;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;box-shadow:rgba(0, 0, 0, 0.1) 0 1px 3px;-moz-box-shadow:rgba(0, 0, 0, 0.1) 0 1px 3px;-webkit-box-shadow:rgba(0, 0, 0, 0.1) 0 1px 3px}#raccoon_tip .rt_content,#raccoon_tip .rt_content a{color:#a27d35;text-shadow:none}#raccoon_tip .rt_content a{outline:0}</style>';
1560
+
1561
+ var default_options = {event: "click", duration: "fast", position: "bottom_right", beforeShow: function() {}, canHide: function() { return true; }, afterHide: function() {}}, opts = null;
1562
+ var displaying = false, mouseover = false;
1563
+
1564
+ var register = function(target, content, options) {
1565
+ var attachFunction = $.inArray(options.event || default_options.event, ["focus"]) == -1 ? "live" : "bind";
1566
+ $(target)[attachFunction]((options || {}).event || "click", function(event) {
1567
+ event.preventDefault();
1568
+ display(event.target, content, options);
1569
+ });
1570
+ };
1571
+
1572
+ var display = function(target, content, options) {
1573
+ displaying = true;
1574
+ setup();
1575
+ deriveOptions(target, content, options);
1576
+ show();
1577
+ displaying = false;
1578
+ };
1579
+
1580
+ var close = function() {
1581
+ hide();
1582
+ };
1583
+
1584
+ var setup = function() {
1585
+ if (!$("#raccoon_tip").length) {
1586
+ $("body").mouseup(function(event) {
1587
+ if (!displaying && !mouseover && opts.canHide.apply()) {
1588
+ hide();
1589
+ }
1590
+ });
1591
+ if (!$("head").length) {
1592
+ $(document.body).before("<head></head>");
1593
+ }
1594
+ $(css).prependTo("head");
1595
+ $(html).appendTo("body").find(".rt_content").mouseenter(function() { mouseover = true; }).mouseleave(function() { mouseover = false; });
1596
+ } else {
1597
+ hide();
1598
+ }
1599
+ };
1600
+
1601
+ var deriveOptions = function(__target__, __content__, options) {
1602
+ opts = $.extend({}, default_options, options, {target: $(__target__), content: $(__content__)});
1603
+ };
1604
+
1605
+ var show = function() {
1606
+ beforeShow();
1607
+ setContent();
1608
+ position();
1609
+ $("#raccoon_tip").data("rt_options", opts).show(opts.duration);
1610
+ };
1611
+
1612
+ var beforeShow = function() {
1613
+ var options = opts.beforeShow.apply(opts.target, [opts.content, opts]);
1614
+ if (options) {
1615
+ $.extend(opts, options);
1616
+ }
1617
+ };
1618
+
1619
+ var setContent = function() {
1620
+ opts.content = $(opts.content);
1621
+ if (opts.content.length) {
1622
+ var marker = null;
1623
+ if (opts.content.context) {
1624
+ marker = $("<span class=\".rt_marker\"></span>");
1625
+ opts.content.before(marker);
1626
+ }
1627
+ opts.content.appendTo("#raccoon_tip .rt_content");
1628
+ $("#raccoon_tip").data("rt_marker", marker);
1629
+ } else {
1630
+ $("#raccoon_tip .rt_content").html(opts.content.selector);
1631
+ }
1632
+ };
1633
+
1634
+ var position = function() {
1635
+ var raccoon_tip = $("#raccoon_tip"),
1636
+ positions = ["bottom_right", "bottom_middle", "bottom_left", "middle_left", "top_left", "top_middle", "top_right", "middle_right"],
1637
+ pos_index = positions.indexOf(opts.position),
1638
+ variants = [];
1639
+
1640
+ for (var i = 0; i < pos_index; i++) {
1641
+ positions.push(positions.shift());
1642
+ }
1643
+
1644
+ for (var direction = 0; direction < 2; direction++) {
1645
+
1646
+ if (direction == 1) {
1647
+ positions.reverse();
1648
+ positions.unshift(positions.pop());
1649
+ }
1650
+
1651
+ for (var i = 0; i < positions.length; i++) {
1652
+ if (direction == 1 && variants[0].index <= i) {
1653
+ variants[direction] = {index: 9};
1654
+ break;
1655
+ }
1656
+
1657
+ raccoon_tip.attr("class", "rt_" + positions[i]);
1658
+
1659
+ for (var axis_index = 0; axis_index < 2; axis_index++) {
1660
+ switch(positions[i].split("_")[axis_index]) {
1661
+ case "top":
1662
+ raccoon_tip.css({top: opts.target.offset().top - raccoon_tip.outerHeight() - 7}); break;
1663
+ case "bottom":
1664
+ raccoon_tip.css({top: opts.target.offset().top + opts.target.outerHeight() + 7}); break;
1665
+ case "left":
1666
+ raccoon_tip.css({left: opts.target.offset().left - raccoon_tip.outerWidth() }); break;
1667
+ case "right":
1668
+ raccoon_tip.css({left: opts.target.offset().left + opts.target.outerWidth() }); break;
1669
+ case "middle":
1670
+ if (axis_index == 0) {
1671
+ raccoon_tip.css({top: opts.target.offset().top + (opts.target.outerHeight() / 2) - (raccoon_tip.outerHeight() / 2)});
1672
+ } else {
1673
+ raccoon_tip.css({left: opts.target.offset().left + (opts.target.outerWidth() / 2) - (raccoon_tip.outerWidth() / 2)});
1674
+ }
1675
+ break;
1676
+ }
1677
+ }
1678
+
1679
+ variants[direction] = {index: i, position: positions[i], top: raccoon_tip.css("top"), left: raccoon_tip.css("left")};
1680
+
1681
+ if (!((parseInt(raccoon_tip.css("top" ), 10) < $(window).scrollTop() ) ||
1682
+ (parseInt(raccoon_tip.css("left"), 10) < $(window).scrollLeft()) ||
1683
+ (parseInt(raccoon_tip.css("top" ), 10) + raccoon_tip.outerHeight() > $(window).scrollTop() + $(window).height()) ||
1684
+ (parseInt(raccoon_tip.css("left"), 10) + raccoon_tip.outerWidth() > $(window).scrollLeft() + $(window).width()))) {
1685
+ break;
1686
+ }
1687
+ }
1688
+ }
1689
+
1690
+ var pos = variants[variants[0].index < variants[1].index ? 0 : 1];
1691
+
1692
+ raccoon_tip.attr("class", "rt_" + pos.position);
1693
+ raccoon_tip.css({top: pos.top, left: pos.left});
1694
+ opts.position = pos.position;
1695
+ };
1696
+
1697
+ var hide = function() {
1698
+ var options = $("#raccoon_tip").data("rt_options");
1699
+ $("#raccoon_tip").hide(0);
1700
+ options.afterHide.apply(options.target, [options.content, options]);
1701
+ if ($("#raccoon_tip").data("rt_marker")) {
1702
+ $("#raccoon_tip").data("rt_marker").before($("#raccoon_tip .rt_content").children()).remove();
1703
+ }
1704
+ };
1705
+
1706
+ return {
1707
+ version: "1.0.8",
1708
+ init: function() {
1709
+ if (typeof(onRaccoonTipReady) == "function") {
1710
+ onRaccoonTipReady();
1711
+ };
1712
+ },
1713
+ register: register,
1714
+ display : display,
1715
+ close : close
1716
+ };
1717
+ }());
1718
+
1719
+ (function requireMissingLibs() {
1720
+ var missing_libs = [];
1721
+
1722
+ if (typeof(jQuery) == "undefined") {
1723
+ missing_libs.push("core");
1724
+ }
1725
+
1726
+ if (missing_libs.length == 0) {
1727
+ RaccoonTip.init();
1728
+ } else {
1729
+ var id = "rt_dummy_script";
1730
+ document.write('<script id="' + id + '"></script>');
1731
+
1732
+ var dummyScript = document.getElementById(id);
1733
+ var element = dummyScript.previousSibling;
1734
+ while (element && element.tagName.toLowerCase() != "script") {
1735
+ element = element.previousSibling;
1736
+ }
1737
+ dummyScript.parentNode.removeChild(dummyScript);
1738
+
1739
+ var src = element.getAttribute("src").replace(/(development\/)?(\w+)(\-min)?\.js.*$/, "jquery/" + missing_libs.sort().join(".") + ".js");
1740
+ document.write('<script src="' + src + '" type="text/javascript" ' +
1741
+ 'onload="RaccoonTip.init()" onreadystatechange="RaccoonTip.init()">' +
1742
+ '</script>');
1743
+ }
1744
+ }());
1745
+
1746
+ }