lokka 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. data/Gemfile +48 -0
  2. data/LICENSE +20 -0
  3. data/README.ja.rdoc +46 -0
  4. data/README.rdoc +46 -0
  5. data/Rakefile +116 -0
  6. data/VERSION +1 -0
  7. data/bin/autotest +16 -0
  8. data/bin/bluefeather +16 -0
  9. data/bin/convert_to_should_syntax +16 -0
  10. data/bin/css2sass +16 -0
  11. data/bin/edit_json.rb +16 -0
  12. data/bin/erubis +16 -0
  13. data/bin/exceptional +16 -0
  14. data/bin/haml +16 -0
  15. data/bin/html2haml +16 -0
  16. data/bin/jeweler +16 -0
  17. data/bin/lokka +4 -0
  18. data/bin/prettify_json.rb +16 -0
  19. data/bin/rackup +16 -0
  20. data/bin/rake +16 -0
  21. data/bin/rcov +16 -0
  22. data/bin/sass +16 -0
  23. data/bin/sass-convert +16 -0
  24. data/bin/tilt +16 -0
  25. data/bin/unit_diff +16 -0
  26. data/config.ru +3 -0
  27. data/config.yml +4 -0
  28. data/i18n/en.yml +149 -0
  29. data/i18n/ja.yml +149 -0
  30. data/init.rb +7 -0
  31. data/install.rb +16 -0
  32. data/lib/lokka.rb +138 -0
  33. data/lib/lokka/app.rb +562 -0
  34. data/lib/lokka/before.rb +29 -0
  35. data/lib/lokka/bread_crumb.rb +34 -0
  36. data/lib/lokka/category.rb +33 -0
  37. data/lib/lokka/comment.rb +26 -0
  38. data/lib/lokka/entry.rb +70 -0
  39. data/lib/lokka/helpers.rb +214 -0
  40. data/lib/lokka/option.rb +23 -0
  41. data/lib/lokka/site.rb +19 -0
  42. data/lib/lokka/tag.rb +5 -0
  43. data/lib/lokka/theme.rb +17 -0
  44. data/lib/lokka/user.rb +77 -0
  45. data/lib/sqlite3.dll +0 -0
  46. data/lokka.exe +0 -0
  47. data/lokka.exy +15 -0
  48. data/lokka.gemspec +373 -0
  49. data/lokka.ico +0 -0
  50. data/lokka.rb +11 -0
  51. data/public/admin/categories/edit.haml +6 -0
  52. data/public/admin/categories/form.haml +18 -0
  53. data/public/admin/categories/index.haml +15 -0
  54. data/public/admin/categories/new.haml +5 -0
  55. data/public/admin/comments/edit.haml +6 -0
  56. data/public/admin/comments/form.haml +29 -0
  57. data/public/admin/comments/index.haml +22 -0
  58. data/public/admin/comments/new.haml +5 -0
  59. data/public/admin/css/editor.css +10 -0
  60. data/public/admin/css/jquery.cleditor.css +24 -0
  61. data/public/admin/css/style.css +710 -0
  62. data/public/admin/edit.haml +42 -0
  63. data/public/admin/favicon.ico +0 -0
  64. data/public/admin/images/add.png +0 -0
  65. data/public/admin/images/aside_arrow.png +0 -0
  66. data/public/admin/images/buttons.gif +0 -0
  67. data/public/admin/images/category.png +0 -0
  68. data/public/admin/images/comment.png +0 -0
  69. data/public/admin/images/dashboard.png +0 -0
  70. data/public/admin/images/file.png +0 -0
  71. data/public/admin/images/files.png +0 -0
  72. data/public/admin/images/mail-attachment.png +0 -0
  73. data/public/admin/images/plugin.png +0 -0
  74. data/public/admin/images/post.png +0 -0
  75. data/public/admin/images/setting.png +0 -0
  76. data/public/admin/images/tag.png +0 -0
  77. data/public/admin/images/theme.png +0 -0
  78. data/public/admin/images/toolbar.gif +0 -0
  79. data/public/admin/images/user.png +0 -0
  80. data/public/admin/index.haml +1 -0
  81. data/public/admin/js/editor.js +7 -0
  82. data/public/admin/js/jquery.cleditor.js +1132 -0
  83. data/public/admin/js/jquery.cleditor.min.js +31 -0
  84. data/public/admin/layout.haml +91 -0
  85. data/public/admin/pages/edit.haml +6 -0
  86. data/public/admin/pages/form.haml +33 -0
  87. data/public/admin/pages/index.haml +23 -0
  88. data/public/admin/pages/new.haml +5 -0
  89. data/public/admin/plugins/index.haml +4 -0
  90. data/public/admin/posts/edit.haml +6 -0
  91. data/public/admin/posts/form.haml +33 -0
  92. data/public/admin/posts/index.haml +23 -0
  93. data/public/admin/posts/new.haml +5 -0
  94. data/public/admin/show.haml +7 -0
  95. data/public/admin/signup.haml +17 -0
  96. data/public/admin/site/edit.haml +13 -0
  97. data/public/admin/tags/edit.haml +6 -0
  98. data/public/admin/tags/form.haml +10 -0
  99. data/public/admin/tags/index.haml +19 -0
  100. data/public/admin/themes/index.haml +13 -0
  101. data/public/admin/users/edit.haml +6 -0
  102. data/public/admin/users/form.haml +22 -0
  103. data/public/admin/users/index.haml +24 -0
  104. data/public/admin/users/new.haml +5 -0
  105. data/public/plugin/lokka-google_analytics/lib/lokka/google_analytics.rb +29 -0
  106. data/public/plugin/lokka-google_analytics/views/index.haml +15 -0
  107. data/public/plugin/lokka-hello/lib/lokka/hello.rb +15 -0
  108. data/public/plugin/lokka-markdown/lib/lokka/markdown.rb +12 -0
  109. data/public/plugin/lokka-rbconfig/lib/lokka/rbconfig.rb +11 -0
  110. data/public/plugin/lokka-rbconfig/views/index.haml +7 -0
  111. data/public/plugin/lokka-rbconfig/views/style.css +12 -0
  112. data/public/system/404.haml +20 -0
  113. data/public/system/500.haml +19 -0
  114. data/public/system/comments/form.haml +22 -0
  115. data/public/system/favicon.ico +0 -0
  116. data/public/system/index.builder +25 -0
  117. data/public/system/style.css +8 -0
  118. data/public/theme/default/entries.erb +40 -0
  119. data/public/theme/default/entry.erb +23 -0
  120. data/public/theme/default/layout.erb +74 -0
  121. data/public/theme/default/quote.gif +0 -0
  122. data/public/theme/default/screenshot.png +0 -0
  123. data/public/theme/default/style.css +1147 -0
  124. data/public/theme/jarvi/entries.erb +26 -0
  125. data/public/theme/jarvi/entry.erb +14 -0
  126. data/public/theme/jarvi/favicon.ico +0 -0
  127. data/public/theme/jarvi/images/aside_dt.gif +0 -0
  128. data/public/theme/jarvi/images/aside_dt.png +0 -0
  129. data/public/theme/jarvi/images/footer.gif +0 -0
  130. data/public/theme/jarvi/images/footer.psd +0 -0
  131. data/public/theme/jarvi/images/header_deascription_ul.gif +0 -0
  132. data/public/theme/jarvi/images/header_language.gif +0 -0
  133. data/public/theme/jarvi/images/header_nav.gif +0 -0
  134. data/public/theme/jarvi/images/html.gif +0 -0
  135. data/public/theme/jarvi/images/index_content.gif +0 -0
  136. data/public/theme/jarvi/images/index_content_footer.gif +0 -0
  137. data/public/theme/jarvi/images/index_content_header.gif +0 -0
  138. data/public/theme/jarvi/images/index_header_nav.gif +0 -0
  139. data/public/theme/jarvi/images/section_header_title.gif +0 -0
  140. data/public/theme/jarvi/images/wide_content.gif +0 -0
  141. data/public/theme/jarvi/images/wide_content_body.gif +0 -0
  142. data/public/theme/jarvi/images/wide_content_h3.gif +0 -0
  143. data/public/theme/jarvi/layout.erb +55 -0
  144. data/public/theme/jarvi/screenshot.png +0 -0
  145. data/public/theme/jarvi/style.css +618 -0
  146. data/public/theme/lokka-org/article.haml +9 -0
  147. data/public/theme/lokka-org/entries.haml +9 -0
  148. data/public/theme/lokka-org/entry.haml +1 -0
  149. data/public/theme/lokka-org/favicon.ico +0 -0
  150. data/public/theme/lokka-org/images/aside_dt.gif +0 -0
  151. data/public/theme/lokka-org/images/aside_dt.png +0 -0
  152. data/public/theme/lokka-org/images/download_button.jpg +0 -0
  153. data/public/theme/lokka-org/images/footer.gif +0 -0
  154. data/public/theme/lokka-org/images/footer.psd +0 -0
  155. data/public/theme/lokka-org/images/header_capture.gif +0 -0
  156. data/public/theme/lokka-org/images/header_capture.jpg +0 -0
  157. data/public/theme/lokka-org/images/header_capture_a.gif +0 -0
  158. data/public/theme/lokka-org/images/header_deascription_ul.gif +0 -0
  159. data/public/theme/lokka-org/images/header_h1_a.gif +0 -0
  160. data/public/theme/lokka-org/images/header_language.gif +0 -0
  161. data/public/theme/lokka-org/images/header_nav.gif +0 -0
  162. data/public/theme/lokka-org/images/heder_nav_home.gif +0 -0
  163. data/public/theme/lokka-org/images/heder_nav_home.jpg +0 -0
  164. data/public/theme/lokka-org/images/html.gif +0 -0
  165. data/public/theme/lokka-org/images/index_content.gif +0 -0
  166. data/public/theme/lokka-org/images/index_content_footer.gif +0 -0
  167. data/public/theme/lokka-org/images/index_content_header.gif +0 -0
  168. data/public/theme/lokka-org/images/index_header_h1.gif +0 -0
  169. data/public/theme/lokka-org/images/index_header_nav.gif +0 -0
  170. data/public/theme/lokka-org/images/language_a.png +0 -0
  171. data/public/theme/lokka-org/images/section_header_title.gif +0 -0
  172. data/public/theme/lokka-org/images/wide_content.gif +0 -0
  173. data/public/theme/lokka-org/images/wide_content_body.gif +0 -0
  174. data/public/theme/lokka-org/images/wide_content_h3.gif +0 -0
  175. data/public/theme/lokka-org/index.haml +5 -0
  176. data/public/theme/lokka-org/layout.haml +72 -0
  177. data/public/theme/lokka-org/quote.gif +0 -0
  178. data/public/theme/lokka-org/screenshot.png +0 -0
  179. data/public/theme/lokka-org/style.css +806 -0
  180. data/public/theme/p0t/article.haml +10 -0
  181. data/public/theme/p0t/entries.haml +9 -0
  182. data/public/theme/p0t/entry.haml +7 -0
  183. data/public/theme/p0t/favicon.ico +0 -0
  184. data/public/theme/p0t/images/quote.gif +0 -0
  185. data/public/theme/p0t/layout.haml +68 -0
  186. data/public/theme/p0t/screenshot.png +0 -0
  187. data/public/theme/p0t/style.css +359 -0
  188. data/public/theme/vicuna-mono/entries.erb +40 -0
  189. data/public/theme/vicuna-mono/entry.erb +23 -0
  190. data/public/theme/vicuna-mono/layout.erb +76 -0
  191. data/public/theme/vicuna-mono/quote.gif +0 -0
  192. data/public/theme/vicuna-mono/screenshot.png +0 -0
  193. data/public/theme/vicuna-mono/style.css +1156 -0
  194. data/test/helper.rb +23 -0
  195. data/test/lokka/app_test.rb +15 -0
  196. data/test/lokka/post_test.rb +17 -0
  197. metadata +965 -0
@@ -0,0 +1,42 @@
1
+ #sinatra_authentication
2
+ #sinatra_authentication_flash= flash[:notice]
3
+ %h1
4
+ Edit
5
+ - if @user.id == current_user.id
6
+ account
7
+ - else
8
+ - if @user.email
9
+ = @user.email
10
+ - elsif @user.fb_uid
11
+ <fb:name uid=#{@user.fb_uid} linked='false' />
12
+ - else
13
+ account
14
+ %form{:action => "/users/#{@user.id}/edit", :method => "post"}
15
+ .field
16
+ .label
17
+ %label{:for => "user_email"} Email
18
+ %input{ :id => "user_email", :name => "user[email]", :size => 30, :type => "text", :value => @user.email }
19
+ .field
20
+ .label
21
+ %label{:for => "user_password"} New password
22
+ %input{ :id => "user_password", :name => "user[password]", :size => 30, :type => "password" }
23
+ .field
24
+ .label
25
+ %label{:for => "user_password_confirmation"} Confirm
26
+ %input{ :id => "user_password_confirmation", :name => "user[password_confirmation]", :size => 30, :type => "password" }
27
+ -# don't render permission field if admin and editing yourself so you don't shoot yourself in the foot
28
+ - if current_user.admin? && current_user.id != @user.id
29
+ .field
30
+ .label
31
+ %label{:for => 'permission_level'} Permission level
32
+ %select{ :id => "permission_level", :name => "user[permission_level]" }
33
+ %option{:value => -1, :selected => @user.admin?}
34
+ Admin
35
+ %option{:value => 1, :selected => @user.permission_level == 1}
36
+ Authenticated user
37
+ .buttons
38
+ %input{ :value => "Update", :type => "submit" }
39
+ - if Sinatra.const_defined?('FacebookObject')
40
+ - unless @user.fb_uid
41
+ |
42
+ = render_facebook_connect_link('Link account with Facebook')
Binary file
@@ -0,0 +1 @@
1
+ %h2= t.dashboard
@@ -0,0 +1,7 @@
1
+ $(function() {
2
+ $('textarea.editor').cleditor({
3
+ width: 550,
4
+ height: 550,
5
+ controls: 'bold italic underline strikethrough subscript superscript font size style | color highlight removeformat bullets numbering | outdent indent | alignleft center alignright justify | rule image link unlink | pastetext source'
6
+ })
7
+ })
@@ -0,0 +1,1132 @@
1
+ /**
2
+ @preserve CLEditor WYSIWYG HTML Editor v1.3.0
3
+ http://premiumsoftware.net/cleditor
4
+ requires jQuery v1.4.2 or later
5
+
6
+ Copyright 2010, Chris Landowski, Premium Software, LLC
7
+ Dual licensed under the MIT or GPL Version 2 licenses.
8
+ */
9
+
10
+ // ==ClosureCompiler==
11
+ // @compilation_level SIMPLE_OPTIMIZATIONS
12
+ // @output_file_name jquery.cleditor.min.js
13
+ // ==/ClosureCompiler==
14
+
15
+ (function($) {
16
+
17
+ //==============
18
+ // jQuery Plugin
19
+ //==============
20
+
21
+ $.cleditor = {
22
+
23
+ // Define the defaults used for all new cleditor instances
24
+ defaultOptions: {
25
+ width: 500, // width not including margins, borders or padding
26
+ height: 250, // height not including margins, borders or padding
27
+ controls: // controls to add to the toolbar
28
+ "bold italic underline strikethrough subscript superscript | font size " +
29
+ "style | color highlight removeformat | bullets numbering | outdent " +
30
+ "indent | alignleft center alignright justify | undo redo | " +
31
+ "rule image link unlink | cut copy paste pastetext | print source",
32
+ colors: // colors in the color popup
33
+ "FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF " +
34
+ "CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F " +
35
+ "BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C " +
36
+ "999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C " +
37
+ "666 900 C60 C93 990 090 399 33F 60C 939 " +
38
+ "333 600 930 963 660 060 366 009 339 636 " +
39
+ "000 300 630 633 330 030 033 006 309 303",
40
+ fonts: // font names in the font popup
41
+ "Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond," +
42
+ "Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana",
43
+ sizes: // sizes in the font size popup
44
+ "1,2,3,4,5,6,7",
45
+ styles: // styles in the style popup
46
+ [["Paragraph", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"],
47
+ ["Header 3", "<h3>"], ["Header 4","<h4>"], ["Header 5","<h5>"],
48
+ ["Header 6","<h6>"]],
49
+ useCSS: false, // use CSS to style HTML when possible (not supported in ie)
50
+ docType: // Document type contained within the editor
51
+ '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
52
+ docCSSFile: // CSS file used to style the document contained within the editor
53
+ "",
54
+ bodyStyle: // style to assign to document body contained within the editor
55
+ "margin:4px; font:10pt Arial,Verdana; cursor:text"
56
+ },
57
+
58
+ // Define all usable toolbar buttons - the init string property is
59
+ // expanded during initialization back into the buttons object and
60
+ // seperate object properties are created for each button.
61
+ // e.g. buttons.size.title = "Font Size"
62
+ buttons: {
63
+ // name,title,command,popupName (""=use name)
64
+ init:
65
+ "bold,,|" +
66
+ "italic,,|" +
67
+ "underline,,|" +
68
+ "strikethrough,,|" +
69
+ "subscript,,|" +
70
+ "superscript,,|" +
71
+ "font,,fontname,|" +
72
+ "size,Font Size,fontsize,|" +
73
+ "style,,formatblock,|" +
74
+ "color,Font Color,forecolor,|" +
75
+ "highlight,Text Highlight Color,hilitecolor,color|" +
76
+ "removeformat,Remove Formatting,|" +
77
+ "bullets,,insertunorderedlist|" +
78
+ "numbering,,insertorderedlist|" +
79
+ "outdent,,|" +
80
+ "indent,,|" +
81
+ "alignleft,Align Text Left,justifyleft|" +
82
+ "center,,justifycenter|" +
83
+ "alignright,Align Text Right,justifyright|" +
84
+ "justify,,justifyfull|" +
85
+ "undo,,|" +
86
+ "redo,,|" +
87
+ "rule,Insert Horizontal Rule,inserthorizontalrule|" +
88
+ "image,Insert Image,insertimage,url|" +
89
+ "link,Insert Hyperlink,createlink,url|" +
90
+ "unlink,Remove Hyperlink,|" +
91
+ "cut,,|" +
92
+ "copy,,|" +
93
+ "paste,,|" +
94
+ "pastetext,Paste as Text,inserthtml,|" +
95
+ "print,,|" +
96
+ "source,Show Source"
97
+ },
98
+
99
+ // imagesPath - returns the path to the images folder
100
+ imagesPath: function() { return imagesPath(); }
101
+
102
+ };
103
+
104
+ // cleditor - creates a new editor for each of the matched textareas
105
+ $.fn.cleditor = function(options) {
106
+
107
+ // Create a new jQuery object to hold the results
108
+ var $result = $([]);
109
+
110
+ // Loop through all matching textareas and create the editors
111
+ this.each(function(idx, elem) {
112
+ if (elem.tagName == "TEXTAREA") {
113
+ var data = $.data(elem, CLEDITOR);
114
+ if (!data) data = new cleditor(elem, options);
115
+ $result = $result.add(data);
116
+ }
117
+ });
118
+
119
+ // return the new jQuery object
120
+ return $result;
121
+
122
+ };
123
+
124
+ //==================
125
+ // Private Variables
126
+ //==================
127
+
128
+ var
129
+
130
+ // Misc constants
131
+ BACKGROUND_COLOR = "backgroundColor",
132
+ BUTTON = "button",
133
+ BUTTON_NAME = "buttonName",
134
+ CHANGE = "change",
135
+ CLEDITOR = "cleditor",
136
+ CLICK = "click",
137
+ DISABLED = "disabled",
138
+ DIV_TAG = "<div>",
139
+ TRANSPARENT = "transparent",
140
+ UNSELECTABLE = "unselectable",
141
+
142
+ // Class name constants
143
+ MAIN_CLASS = "cleditorMain", // main containing div
144
+ TOOLBAR_CLASS = "cleditorToolbar", // toolbar div inside main div
145
+ GROUP_CLASS = "cleditorGroup", // group divs inside the toolbar div
146
+ BUTTON_CLASS = "cleditorButton", // button divs inside group div
147
+ DISABLED_CLASS = "cleditorDisabled",// disabled button divs
148
+ DIVIDER_CLASS = "cleditorDivider", // divider divs inside group div
149
+ POPUP_CLASS = "cleditorPopup", // popup divs inside body
150
+ LIST_CLASS = "cleditorList", // list popup divs inside body
151
+ COLOR_CLASS = "cleditorColor", // color popup div inside body
152
+ PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body
153
+ MSG_CLASS = "cleditorMsg", // message popup div inside body
154
+
155
+ // Test for ie
156
+ ie = $.browser.msie,
157
+ ie6 = /msie\s6/i.test(navigator.userAgent),
158
+
159
+ // Test for iPhone/iTouch/iPad
160
+ iOS = /iphone|ipad|ipod/i.test(navigator.userAgent),
161
+
162
+ // Popups are created once as needed and shared by all editor instances
163
+ popups = {},
164
+
165
+ // Used to prevent the document click event from being bound more than once
166
+ documentClickAssigned,
167
+
168
+ // Local copy of the buttons object
169
+ buttons = $.cleditor.buttons;
170
+
171
+ //===============
172
+ // Initialization
173
+ //===============
174
+
175
+ // Expand the buttons.init string back into the buttons object
176
+ // and create seperate object properties for each button.
177
+ // e.g. buttons.size.title = "Font Size"
178
+ $.each(buttons.init.split("|"), function(idx, button) {
179
+ var items = button.split(","), name = items[0];
180
+ buttons[name] = {
181
+ stripIndex: idx,
182
+ name: name,
183
+ title: items[1] === "" ? name.charAt(0).toUpperCase() + name.substr(1) : items[1],
184
+ command: items[2] === "" ? name : items[2],
185
+ popupName: items[3] === "" ? name : items[3]
186
+ };
187
+ });
188
+ delete buttons.init;
189
+
190
+ //============
191
+ // Constructor
192
+ //============
193
+
194
+ // cleditor - creates a new editor for the passed in textarea element
195
+ cleditor = function(area, options) {
196
+
197
+ var editor = this;
198
+
199
+ // Get the defaults and override with options
200
+ editor.options = options = $.extend({}, $.cleditor.defaultOptions, options);
201
+
202
+ // Hide the textarea and associate it with this editor
203
+ var $area = editor.$area = $(area)
204
+ .hide()
205
+ .data(CLEDITOR, editor)
206
+ .blur(function() {
207
+ // Update the iframe when the textarea loses focus
208
+ updateFrame(editor, true);
209
+ });
210
+
211
+ // Create the main container and append the textarea
212
+ var $main = editor.$main = $(DIV_TAG)
213
+ .addClass(MAIN_CLASS)
214
+ .width(options.width)
215
+ .height(options.height);
216
+
217
+ // Create the toolbar
218
+ var $toolbar = editor.$toolbar = $(DIV_TAG)
219
+ .addClass(TOOLBAR_CLASS)
220
+ .appendTo($main);
221
+
222
+ // Add the first group to the toolbar
223
+ var $group = $(DIV_TAG)
224
+ .addClass(GROUP_CLASS)
225
+ .appendTo($toolbar);
226
+
227
+ // Add the buttons to the toolbar
228
+ $.each(options.controls.split(" "), function(idx, buttonName) {
229
+ if (buttonName === "") return true;
230
+
231
+ // Divider
232
+ if (buttonName == "|") {
233
+
234
+ // Add a new divider to the group
235
+ var $div = $(DIV_TAG)
236
+ .addClass(DIVIDER_CLASS)
237
+ .appendTo($group);
238
+
239
+ // Create a new group
240
+ $group = $(DIV_TAG)
241
+ .addClass(GROUP_CLASS)
242
+ .appendTo($toolbar);
243
+
244
+ }
245
+
246
+ // Button
247
+ else {
248
+
249
+ // Get the button definition
250
+ var button = buttons[buttonName];
251
+
252
+ // Add a new button to the group
253
+ var $buttonDiv = $(DIV_TAG)
254
+ .data(BUTTON_NAME, button.name)
255
+ .addClass(BUTTON_CLASS)
256
+ .attr("title", button.title)
257
+ .bind(CLICK, $.proxy(buttonClick, editor))
258
+ .appendTo($group)
259
+ .hover(hoverEnter, hoverLeave);
260
+
261
+ // Prepare the button image
262
+ var map = {};
263
+ if (button.css) map = button.css;
264
+ else if (button.image) map.backgroundImage = imageUrl(button.image);
265
+ if (button.stripIndex) map.backgroundPosition = button.stripIndex * -24;
266
+ $buttonDiv.css(map);
267
+
268
+ // Add the unselectable attribute for ie
269
+ if (ie)
270
+ $buttonDiv.attr(UNSELECTABLE, "on");
271
+
272
+ // Create the popup
273
+ if (button.popupName)
274
+ createPopup(button.popupName, options, button.popupClass,
275
+ button.popupContent, button.popupHover);
276
+
277
+ }
278
+
279
+ });
280
+
281
+ // Add the main div to the DOM and append the textarea
282
+ $main.insertBefore($area)
283
+ .append($area);
284
+
285
+ // Bind the document click event handler
286
+ if (!documentClickAssigned) {
287
+ $(document).click(function(e) {
288
+ // Dismiss all non-prompt popups
289
+ var $target = $(e.target);
290
+ if (!$target.add($target.parents()).is("." + PROMPT_CLASS))
291
+ hidePopups();
292
+ });
293
+ documentClickAssigned = true;
294
+ }
295
+
296
+ // Bind the window resize event when the width or height is auto or %
297
+ if (/auto|%/.test("" + options.width + options.height))
298
+ $(window).resize(function() {refresh(editor);});
299
+
300
+ // Create the iframe and resize the controls
301
+ refresh(editor);
302
+
303
+ };
304
+
305
+ //===============
306
+ // Public Methods
307
+ //===============
308
+
309
+ var fn = cleditor.prototype,
310
+
311
+ // Expose the following private functions as methods on the cleditor object.
312
+ // The closure compiler will rename the private functions. However, the
313
+ // exposed method names on the cleditor object will remain fixed.
314
+ methods = [
315
+ ["clear", clear],
316
+ ["disable", disable],
317
+ ["execCommand", execCommand],
318
+ ["focus", focus],
319
+ ["hidePopups", hidePopups],
320
+ ["sourceMode", sourceMode, true],
321
+ ["refresh", refresh],
322
+ ["select", select],
323
+ ["selectedHTML", selectedHTML, true],
324
+ ["selectedText", selectedText, true],
325
+ ["showMessage", showMessage],
326
+ ["updateFrame", updateFrame],
327
+ ["updateTextArea", updateTextArea]
328
+ ];
329
+
330
+ $.each(methods, function(idx, method) {
331
+ fn[method[0]] = function() {
332
+ var editor = this, args = [editor];
333
+ // using each here would cast booleans into objects!
334
+ for(var x = 0; x < arguments.length; x++) {args.push(arguments[x]);}
335
+ var result = method[1].apply(editor, args);
336
+ if (method[2]) return result;
337
+ return editor;
338
+ };
339
+ });
340
+
341
+ // change - shortcut for .bind("change", handler) or .trigger("change")
342
+ fn.change = function(handler) {
343
+ var $this = $(this);
344
+ return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE);
345
+ };
346
+
347
+ //===============
348
+ // Event Handlers
349
+ //===============
350
+
351
+ // buttonClick - click event handler for toolbar buttons
352
+ function buttonClick(e) {
353
+
354
+ var editor = this,
355
+ buttonDiv = e.target,
356
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
357
+ button = buttons[buttonName],
358
+ popupName = button.popupName,
359
+ popup = popups[popupName];
360
+
361
+ // Check if disabled
362
+ if (editor.disabled || $(buttonDiv).attr(DISABLED) == DISABLED)
363
+ return;
364
+
365
+ // Fire the buttonClick event
366
+ var data = {
367
+ editor: editor,
368
+ button: buttonDiv,
369
+ buttonName: buttonName,
370
+ popup: popup,
371
+ popupName: popupName,
372
+ command: button.command,
373
+ useCSS: editor.options.useCSS
374
+ };
375
+
376
+ if (button.buttonClick && button.buttonClick(e, data) === false)
377
+ return false;
378
+
379
+ // Toggle source
380
+ if (buttonName == "source") {
381
+
382
+ // Show the iframe
383
+ if (sourceMode(editor)) {
384
+ delete editor.range;
385
+ editor.$area.hide();
386
+ editor.$frame.show();
387
+ buttonDiv.title = button.title;
388
+ }
389
+
390
+ // Show the textarea
391
+ else {
392
+ editor.$frame.hide();
393
+ editor.$area.show();
394
+ buttonDiv.title = "Show Rich Text";
395
+ }
396
+
397
+ // Enable or disable the toolbar buttons
398
+ // IE requires the timeout
399
+ setTimeout(function() {refreshButtons(editor);}, 100);
400
+
401
+ }
402
+
403
+ // Check for rich text mode
404
+ else if (!sourceMode(editor)) {
405
+
406
+ // Handle popups
407
+ if (popupName) {
408
+ var $popup = $(popup);
409
+
410
+ // URL
411
+ if (popupName == "url") {
412
+
413
+ // Check for selection before showing the link url popup
414
+ if (buttonName == "link" && selectedText(editor) === "") {
415
+ showMessage(editor, "A selection is required when inserting a link.", buttonDiv);
416
+ return false;
417
+ }
418
+
419
+ // Wire up the submit button click event handler
420
+ $popup.children(":button")
421
+ .unbind(CLICK)
422
+ .bind(CLICK, function() {
423
+
424
+ // Insert the image or link if a url was entered
425
+ var $text = $popup.find(":text"),
426
+ url = $.trim($text.val());
427
+ if (url !== "")
428
+ execCommand(editor, data.command, url, null, data.button);
429
+
430
+ // Reset the text, hide the popup and set focus
431
+ $text.val("http://");
432
+ hidePopups();
433
+ focus(editor);
434
+
435
+ });
436
+
437
+ }
438
+
439
+ // Paste as Text
440
+ else if (popupName == "pastetext") {
441
+
442
+ // Wire up the submit button click event handler
443
+ $popup.children(":button")
444
+ .unbind(CLICK)
445
+ .bind(CLICK, function() {
446
+
447
+ // Insert the unformatted text replacing new lines with break tags
448
+ var $textarea = $popup.find("textarea"),
449
+ text = $textarea.val().replace(/\n/g, "<br />");
450
+ if (text !== "")
451
+ execCommand(editor, data.command, text, null, data.button);
452
+
453
+ // Reset the text, hide the popup and set focus
454
+ $textarea.val("");
455
+ hidePopups();
456
+ focus(editor);
457
+
458
+ });
459
+
460
+ }
461
+
462
+ // Show the popup if not already showing for this button
463
+ if (buttonDiv !== $.data(popup, BUTTON)) {
464
+ showPopup(editor, popup, buttonDiv);
465
+ return false; // stop propagination to document click
466
+ }
467
+
468
+ // propaginate to documnt click
469
+ return;
470
+
471
+ }
472
+
473
+ // Print
474
+ else if (buttonName == "print")
475
+ editor.$frame[0].contentWindow.print();
476
+
477
+ // All other buttons
478
+ else if (!execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
479
+ return false;
480
+
481
+ }
482
+
483
+ // Focus the editor
484
+ focus(editor);
485
+
486
+ }
487
+
488
+ // hoverEnter - mouseenter event handler for buttons and popup items
489
+ function hoverEnter(e) {
490
+ var $div = $(e.target).closest("div");
491
+ $div.css(BACKGROUND_COLOR, $div.data(BUTTON_NAME) ? "#FFF" : "#FFC");
492
+ }
493
+
494
+ // hoverLeave - mouseleave event handler for buttons and popup items
495
+ function hoverLeave(e) {
496
+ $(e.target).closest("div").css(BACKGROUND_COLOR, "transparent");
497
+ }
498
+
499
+ // popupClick - click event handler for popup items
500
+ function popupClick(e) {
501
+
502
+ var editor = this,
503
+ popup = e.data.popup,
504
+ target = e.target;
505
+
506
+ // Check for message and prompt popups
507
+ if (popup === popups.msg || $(popup).hasClass(PROMPT_CLASS))
508
+ return;
509
+
510
+ // Get the button info
511
+ var buttonDiv = $.data(popup, BUTTON),
512
+ buttonName = $.data(buttonDiv, BUTTON_NAME),
513
+ button = buttons[buttonName],
514
+ command = button.command,
515
+ value,
516
+ useCSS = editor.options.useCSS;
517
+
518
+ // Get the command value
519
+ if (buttonName == "font")
520
+ // Opera returns the fontfamily wrapped in quotes
521
+ value = target.style.fontFamily.replace(/"/g, "");
522
+ else if (buttonName == "size") {
523
+ if (target.tagName == "DIV")
524
+ target = target.children[0];
525
+ value = target.innerHTML;
526
+ }
527
+ else if (buttonName == "style")
528
+ value = "<" + target.tagName + ">";
529
+ else if (buttonName == "color")
530
+ value = hex(target.style.backgroundColor);
531
+ else if (buttonName == "highlight") {
532
+ value = hex(target.style.backgroundColor);
533
+ if (ie) command = 'backcolor';
534
+ else useCSS = true;
535
+ }
536
+
537
+ // Fire the popupClick event
538
+ var data = {
539
+ editor: editor,
540
+ button: buttonDiv,
541
+ buttonName: buttonName,
542
+ popup: popup,
543
+ popupName: button.popupName,
544
+ command: command,
545
+ value: value,
546
+ useCSS: useCSS
547
+ };
548
+
549
+ if (button.popupClick && button.popupClick(e, data) === false)
550
+ return;
551
+
552
+ // Execute the command
553
+ if (data.command && !execCommand(editor, data.command, data.value, data.useCSS, buttonDiv))
554
+ return false;
555
+
556
+ // Hide the popup and focus the editor
557
+ hidePopups();
558
+ focus(editor);
559
+
560
+ }
561
+
562
+ //==================
563
+ // Private Functions
564
+ //==================
565
+
566
+ // checksum - returns a checksum using the Adler-32 method
567
+ function checksum(text)
568
+ {
569
+ var a = 1, b = 0;
570
+ for (var index = 0; index < text.length; ++index) {
571
+ a = (a + text.charCodeAt(index)) % 65521;
572
+ b = (b + a) % 65521;
573
+ }
574
+ return (b << 16) | a;
575
+ }
576
+
577
+ // clear - clears the contents of the editor
578
+ function clear(editor) {
579
+ editor.$area.val("");
580
+ updateFrame(editor);
581
+ }
582
+
583
+ // createPopup - creates a popup and adds it to the body
584
+ function createPopup(popupName, options, popupTypeClass, popupContent, popupHover) {
585
+
586
+ // Check if popup already exists
587
+ if (popups[popupName])
588
+ return popups[popupName];
589
+
590
+ // Create the popup
591
+ var $popup = $(DIV_TAG)
592
+ .hide()
593
+ .addClass(POPUP_CLASS)
594
+ .appendTo("body");
595
+
596
+ // Add the content
597
+
598
+ // Custom popup
599
+ if (popupContent)
600
+ $popup.html(popupContent);
601
+
602
+ // Color
603
+ else if (popupName == "color") {
604
+ var colors = options.colors.split(" ");
605
+ if (colors.length < 10)
606
+ $popup.width("auto");
607
+ $.each(colors, function(idx, color) {
608
+ $(DIV_TAG).appendTo($popup)
609
+ .css(BACKGROUND_COLOR, "#" + color);
610
+ });
611
+ popupTypeClass = COLOR_CLASS;
612
+ }
613
+
614
+ // Font
615
+ else if (popupName == "font")
616
+ $.each(options.fonts.split(","), function(idx, font) {
617
+ $(DIV_TAG).appendTo($popup)
618
+ .css("fontFamily", font)
619
+ .html(font);
620
+ });
621
+
622
+ // Size
623
+ else if (popupName == "size")
624
+ $.each(options.sizes.split(","), function(idx, size) {
625
+ $(DIV_TAG).appendTo($popup)
626
+ .html("<font size=" + size + ">" + size + "</font>");
627
+ });
628
+
629
+ // Style
630
+ else if (popupName == "style")
631
+ $.each(options.styles, function(idx, style) {
632
+ $(DIV_TAG).appendTo($popup)
633
+ .html(style[1] + style[0] + style[1].replace("<", "</"));
634
+ });
635
+
636
+ // URL
637
+ else if (popupName == "url") {
638
+ $popup.html('Enter URL:<br><input type=text value="http://" size=35><br><input type=button value="Submit">');
639
+ popupTypeClass = PROMPT_CLASS;
640
+ }
641
+
642
+ // Paste as Text
643
+ else if (popupName == "pastetext") {
644
+ $popup.html('Paste your content here and click submit.<br /><textarea cols=40 rows=3></textarea><br /><input type=button value=Submit>');
645
+ popupTypeClass = PROMPT_CLASS;
646
+ }
647
+
648
+ // Add the popup type class name
649
+ if (!popupTypeClass && !popupContent)
650
+ popupTypeClass = LIST_CLASS;
651
+ $popup.addClass(popupTypeClass);
652
+
653
+ // Add the unselectable attribute to all items
654
+ if (ie) {
655
+ $popup.attr(UNSELECTABLE, "on")
656
+ .find("div,font,p,h1,h2,h3,h4,h5,h6")
657
+ .attr(UNSELECTABLE, "on");
658
+ }
659
+
660
+ // Add the hover effect to all items
661
+ if ($popup.hasClass(LIST_CLASS) || popupHover === true)
662
+ $popup.children().hover(hoverEnter, hoverLeave);
663
+
664
+ // Add the popup to the array and return it
665
+ popups[popupName] = $popup[0];
666
+ return $popup[0];
667
+
668
+ }
669
+
670
+ // disable - enables or disables the editor
671
+ function disable(editor, disabled) {
672
+
673
+ // Update the textarea and save the state
674
+ if (disabled) {
675
+ editor.$area.attr(DISABLED, DISABLED);
676
+ editor.disabled = true;
677
+ }
678
+ else {
679
+ editor.$area.removeAttr(DISABLED);
680
+ delete editor.disabled;
681
+ }
682
+
683
+ // Switch the iframe into design mode.
684
+ // ie6 does not support designMode.
685
+ // ie7 & ie8 do not properly support designMode="off".
686
+ try {
687
+ if (ie) editor.doc.body.contentEditable = !disabled;
688
+ else editor.doc.designMode = !disabled ? "on" : "off";
689
+ }
690
+ // Firefox 1.5 throws an exception that can be ignored
691
+ // when toggling designMode from off to on.
692
+ catch (err) {}
693
+
694
+ // Enable or disable the toolbar buttons
695
+ refreshButtons(editor);
696
+
697
+ }
698
+
699
+ // execCommand - executes a designMode command
700
+ function execCommand(editor, command, value, useCSS, button) {
701
+
702
+ // Restore the current ie selection
703
+ restoreRange(editor);
704
+
705
+ // Set the styling method
706
+ if (!ie) {
707
+ if (useCSS === undefined || useCSS === null)
708
+ useCSS = editor.options.useCSS;
709
+ editor.doc.execCommand("styleWithCSS", 0, useCSS.toString());
710
+ }
711
+
712
+ // Execute the command and check for error
713
+ var success = true, description;
714
+ if (ie && command.toLowerCase() == "inserthtml")
715
+ getRange(editor).pasteHTML(value);
716
+ else {
717
+ try { success = editor.doc.execCommand(command, 0, value || null); }
718
+ catch (err) { description = err.description; success = false; }
719
+ if (!success) {
720
+ if ("cutcopypaste".indexOf(command) > -1)
721
+ showMessage(editor, "For security reasons, your browser does not support the " +
722
+ command + " command. Try using the keyboard shortcut or context menu instead.",
723
+ button);
724
+ else
725
+ showMessage(editor,
726
+ (description ? description : "Error executing the " + command + " command."),
727
+ button);
728
+ }
729
+ }
730
+
731
+ // Enable the buttons
732
+ refreshButtons(editor);
733
+ return success;
734
+
735
+ }
736
+
737
+ // focus - sets focus to either the textarea or iframe
738
+ function focus(editor) {
739
+ setTimeout(function() {
740
+ if (sourceMode(editor)) editor.$area.focus();
741
+ else editor.$frame[0].contentWindow.focus();
742
+ refreshButtons(editor);
743
+ }, 0);
744
+ }
745
+
746
+ // getRange - gets the current text range object
747
+ function getRange(editor) {
748
+ if (ie) return getSelection(editor).createRange();
749
+ return getSelection(editor).getRangeAt(0);
750
+ }
751
+
752
+ // getSelection - gets the current text range object
753
+ function getSelection(editor) {
754
+ if (ie) return editor.doc.selection;
755
+ return editor.$frame[0].contentWindow.getSelection();
756
+ }
757
+
758
+ // Returns the hex value for the passed in string.
759
+ // hex("rgb(255, 0, 0)"); // #FF0000
760
+ // hex("#FF0000"); // #FF0000
761
+ // hex("#F00"); // #FF0000
762
+ function hex(s) {
763
+ var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s),
764
+ c = s.split("");
765
+ if (m) {
766
+ s = ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16);
767
+ while (s.length < 6)
768
+ s = "0" + s;
769
+ }
770
+ return "#" + (s.length == 6 ? s : c[1] + c[1] + c[2] + c[2] + c[3] + c[3]);
771
+ }
772
+
773
+ // hidePopups - hides all popups
774
+ function hidePopups() {
775
+ $.each(popups, function(idx, popup) {
776
+ $(popup)
777
+ .hide()
778
+ .unbind(CLICK)
779
+ .removeData(BUTTON);
780
+ });
781
+ }
782
+
783
+ // imagesPath - returns the path to the images folder
784
+ function imagesPath() {
785
+ var cssFile = "jquery.cleditor.css",
786
+ href = $("link[href$='" + cssFile +"']").attr("href");
787
+ return href.substr(0, href.length - cssFile.length) + "images/";
788
+ }
789
+
790
+ // imageUrl - Returns the css url string for a filemane
791
+ function imageUrl(filename) {
792
+ return "url(" + imagesPath() + filename + ")";
793
+ }
794
+
795
+ // refresh - creates the iframe and resizes the controls
796
+ function refresh(editor) {
797
+
798
+ var $main = editor.$main,
799
+ options = editor.options;
800
+
801
+ // Remove the old iframe
802
+ if (editor.$frame)
803
+ editor.$frame.remove();
804
+
805
+ // Create a new iframe
806
+ var $frame = editor.$frame = $('<iframe frameborder="0" src="javascript:true;">')
807
+ .hide()
808
+ .appendTo($main);
809
+
810
+ // Load the iframe document content
811
+ var contentWindow = $frame[0].contentWindow,
812
+ doc = editor.doc = contentWindow.document,
813
+ $doc = $(doc);
814
+
815
+ doc.open();
816
+ doc.write(
817
+ options.docType +
818
+ '<html>' +
819
+ ((options.docCSSFile === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCSSFile + '" /></head>') +
820
+ '<body style="' + options.bodyStyle + '"></body></html>'
821
+ );
822
+ doc.close();
823
+
824
+ // Work around for bug in IE which causes the editor to lose
825
+ // focus when clicking below the end of the document.
826
+ if (ie)
827
+ $doc.click(function() {focus(editor);});
828
+
829
+ // Load the content
830
+ updateFrame(editor);
831
+
832
+ // Bind the ie specific iframe event handlers
833
+ if (ie) {
834
+
835
+ // Save the current user selection. This code is needed since IE will
836
+ // reset the selection just after the beforedeactivate event and just
837
+ // before the beforeactivate event.
838
+ $doc.bind("beforedeactivate beforeactivate selectionchange keypress", function(e) {
839
+
840
+ // Flag the editor as inactive
841
+ if (e.type == "beforedeactivate")
842
+ editor.inactive = true;
843
+
844
+ // Get rid of the bogus selection and flag the editor as active
845
+ else if (e.type == "beforeactivate") {
846
+ if (!editor.inactive && editor.range && editor.range.length > 1)
847
+ editor.range.shift();
848
+ delete editor.inactive;
849
+ }
850
+
851
+ // Save the selection when the editor is active
852
+ else if (!editor.inactive) {
853
+ if (!editor.range)
854
+ editor.range = [];
855
+ editor.range.unshift(getRange(editor));
856
+
857
+ // We only need the last 2 selections
858
+ while (editor.range.length > 2)
859
+ editor.range.pop();
860
+ }
861
+
862
+ });
863
+
864
+ // Restore the text range when the iframe gains focus
865
+ $frame.focus(function() {
866
+ restoreRange(editor);
867
+ });
868
+
869
+ }
870
+
871
+ // Update the textarea when the iframe loses focus
872
+ ($.browser.mozilla ? $doc : $(contentWindow)).blur(function() {
873
+ updateTextArea(editor, true);
874
+ });
875
+
876
+ // Enable the toolbar buttons as the user types or clicks
877
+ $doc.click(hidePopups)
878
+ .bind("keyup mouseup", function() {
879
+ refreshButtons(editor);
880
+ });
881
+
882
+ // Show the textarea for iPhone/iTouch/iPad or
883
+ // the iframe when design mode is supported.
884
+ if (iOS) editor.$area.show();
885
+ else $frame.show();
886
+
887
+ // Wait for the layout to finish - shortcut for $(document).ready()
888
+ $(function() {
889
+
890
+ var $toolbar = editor.$toolbar,
891
+ $group = $toolbar.children("div:last"),
892
+ wid = $main.width();
893
+
894
+ // Resize the toolbar
895
+ var hgt = $group.offset().top + $group.outerHeight() - $toolbar.offset().top + 1;
896
+ $toolbar.height(hgt);
897
+
898
+ // Resize the iframe
899
+ hgt = (/%/.test("" + options.height) ? $main.height() : parseInt(options.height)) - hgt;
900
+ $frame.width(wid).height(hgt);
901
+
902
+ // Resize the textarea. IE6 textareas have a 1px top
903
+ // & bottom margin that cannot be removed using css.
904
+ editor.$area.width(wid).height(ie6 ? hgt - 2 : hgt);
905
+
906
+ // Switch the iframe into design mode if enabled
907
+ disable(editor, editor.disabled);
908
+
909
+ // Enable or disable the toolbar buttons
910
+ refreshButtons(editor);
911
+
912
+ });
913
+
914
+ }
915
+
916
+ // refreshButtons - enables or disables buttons based on availability
917
+ function refreshButtons(editor) {
918
+
919
+ // Webkit requires focus before queryCommandEnabled will return anything but false
920
+ if (!iOS && $.browser.webkit && !editor.focused) {
921
+ editor.$frame[0].contentWindow.focus();
922
+ window.focus();
923
+ editor.focused = true;
924
+ }
925
+
926
+ // Get the object used for checking queryCommandEnabled
927
+ var queryObj = editor.doc;
928
+ if (ie) queryObj = getRange(editor);
929
+
930
+ // Loop through each button
931
+ var inSourceMode = sourceMode(editor);
932
+ $.each(editor.$toolbar.find("." + BUTTON_CLASS), function(idx, elem) {
933
+
934
+ var $elem = $(elem),
935
+ button = $.cleditor.buttons[$.data(elem, BUTTON_NAME)],
936
+ command = button.command,
937
+ enabled = true;
938
+
939
+ // Determine the state
940
+ if (editor.disabled)
941
+ enabled = false;
942
+ else if (button.getEnabled) {
943
+ var data = {
944
+ editor: editor,
945
+ button: elem,
946
+ buttonName: button.name,
947
+ popup: popups[button.popupName],
948
+ popupName: button.popupName,
949
+ command: button.command,
950
+ useCSS: editor.options.useCSS
951
+ };
952
+ enabled = button.getEnabled(data);
953
+ if (enabled === undefined)
954
+ enabled = true;
955
+ }
956
+ else if (((inSourceMode || iOS) && button.name != "source") ||
957
+ (ie && (command == "undo" || command == "redo")))
958
+ enabled = false;
959
+ else if (command && command != "print") {
960
+ if (ie && command == "hilitecolor")
961
+ command = "backcolor";
962
+ // IE does not support inserthtml, so it's always enabled
963
+ if (!ie || command != "inserthtml") {
964
+ try {enabled = queryObj.queryCommandEnabled(command);}
965
+ catch (err) {enabled = false;}
966
+ }
967
+ }
968
+
969
+ // Enable or disable the button
970
+ if (enabled) {
971
+ $elem.removeClass(DISABLED_CLASS);
972
+ $elem.removeAttr(DISABLED);
973
+ }
974
+ else {
975
+ $elem.addClass(DISABLED_CLASS);
976
+ $elem.attr(DISABLED, DISABLED);
977
+ }
978
+
979
+ });
980
+ }
981
+
982
+ // restoreRange - restores the current ie selection
983
+ function restoreRange(editor) {
984
+ if (ie && editor.range)
985
+ editor.range[0].select();
986
+ }
987
+
988
+ // select - selects all the text in either the textarea or iframe
989
+ function select(editor) {
990
+ setTimeout(function() {
991
+ if (sourceMode(editor)) editor.$area.select();
992
+ else execCommand(editor, "selectall");
993
+ }, 0);
994
+ }
995
+
996
+ // selectedHTML - returns the current HTML selection or and empty string
997
+ function selectedHTML(editor) {
998
+ restoreRange(editor);
999
+ var range = getRange(editor);
1000
+ if (ie)
1001
+ return range.htmlText;
1002
+ var layer = $("<layer>")[0];
1003
+ layer.appendChild(range.cloneContents());
1004
+ var html = layer.innerHTML;
1005
+ layer = null;
1006
+ return html;
1007
+ }
1008
+
1009
+ // selectedText - returns the current text selection or and empty string
1010
+ function selectedText(editor) {
1011
+ restoreRange(editor);
1012
+ if (ie) return getRange(editor).text;
1013
+ return getSelection(editor).toString();
1014
+ }
1015
+
1016
+ // showMessage - alert replacement
1017
+ function showMessage(editor, message, button) {
1018
+ var popup = createPopup("msg", editor.options, MSG_CLASS);
1019
+ popup.innerHTML = message;
1020
+ showPopup(editor, popup, button);
1021
+ }
1022
+
1023
+ // showPopup - shows a popup
1024
+ function showPopup(editor, popup, button) {
1025
+
1026
+ var offset, left, top, $popup = $(popup);
1027
+
1028
+ // Determine the popup location
1029
+ if (button) {
1030
+ var $button = $(button);
1031
+ offset = $button.offset();
1032
+ left = --offset.left;
1033
+ top = offset.top + $button.height();
1034
+ }
1035
+ else {
1036
+ var $toolbar = editor.$toolbar;
1037
+ offset = $toolbar.offset();
1038
+ left = Math.floor(($toolbar.width() - $popup.width()) / 2) + offset.left;
1039
+ top = offset.top + $toolbar.height() - 2;
1040
+ }
1041
+
1042
+ // Position and show the popup
1043
+ hidePopups();
1044
+ $popup.css({left: left, top: top})
1045
+ .show();
1046
+
1047
+ // Assign the popup button and click event handler
1048
+ if (button) {
1049
+ $.data(popup, BUTTON, button);
1050
+ $popup.bind(CLICK, {popup: popup}, $.proxy(popupClick, editor));
1051
+ }
1052
+
1053
+ // Focus the first input element if any
1054
+ setTimeout(function() {
1055
+ $popup.find(":text,textarea").eq(0).focus().select();
1056
+ }, 100);
1057
+
1058
+ }
1059
+
1060
+ // sourceMode - returns true if the textarea is showing
1061
+ function sourceMode(editor) {
1062
+ return editor.$area.is(":visible");
1063
+ }
1064
+
1065
+ // updateFrame - updates the iframe with the textarea contents
1066
+ function updateFrame(editor, checkForChange) {
1067
+
1068
+ var code = editor.$area.val(),
1069
+ options = editor.options,
1070
+ updateFrameCallback = options.updateFrame,
1071
+ $body = $(editor.doc.body);
1072
+
1073
+ // Check for textarea change to avoid unnecessary firing
1074
+ // of potentially heavy updateFrame callbacks.
1075
+ if (updateFrameCallback) {
1076
+ var sum = checksum(code);
1077
+ if (checkForChange && editor.areaChecksum == sum)
1078
+ return;
1079
+ editor.areaChecksum = sum;
1080
+ }
1081
+
1082
+ // Convert the textarea source code into iframe html
1083
+ var html = updateFrameCallback ? updateFrameCallback(code) : code;
1084
+
1085
+ // Prevent script injection attacks by html encoding script tags
1086
+ html = html.replace(/<(?=\/?script)/ig, "&lt;");
1087
+
1088
+ // Update the iframe checksum
1089
+ if (options.updateTextArea)
1090
+ editor.frameChecksum = checksum(html);
1091
+
1092
+ // Update the iframe and trigger the change event
1093
+ if (html != $body.html()) {
1094
+ $body.html(html);
1095
+ $(editor).triggerHandler(CHANGE);
1096
+ }
1097
+
1098
+ }
1099
+
1100
+ // updateTextArea - updates the textarea with the iframe contents
1101
+ function updateTextArea(editor, checkForChange) {
1102
+
1103
+ var html = $(editor.doc.body).html(),
1104
+ options = editor.options,
1105
+ updateTextAreaCallback = options.updateTextArea,
1106
+ $area = editor.$area;
1107
+
1108
+ // Check for iframe change to avoid unnecessary firing
1109
+ // of potentially heavy updateTextArea callbacks.
1110
+ if (updateTextAreaCallback) {
1111
+ var sum = checksum(html);
1112
+ if (checkForChange && editor.frameChecksum == sum)
1113
+ return;
1114
+ editor.frameChecksum = sum;
1115
+ }
1116
+
1117
+ // Convert the iframe html into textarea source code
1118
+ var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html;
1119
+
1120
+ // Update the textarea checksum
1121
+ if (options.updateFrame)
1122
+ editor.areaChecksum = checksum(code);
1123
+
1124
+ // Update the textarea and trigger the change event
1125
+ if (code != $area.val()) {
1126
+ $area.val(code);
1127
+ $(editor).triggerHandler(CHANGE);
1128
+ }
1129
+
1130
+ }
1131
+
1132
+ })(jQuery);