rich_cms 2.1.7 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }