transit 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. data/Gemfile +19 -11
  2. data/app/assets/images/transit/icon24x24.png +0 -0
  3. data/app/assets/images/transit/icon24x24_files.png +0 -0
  4. data/app/assets/images/transit/jplayer.swf +0 -0
  5. data/app/assets/images/transit/uploadify.swf +0 -0
  6. data/app/assets/images/transit/video_player.swf +0 -0
  7. data/app/assets/javascripts/jqtools/expose.js +224 -0
  8. data/app/assets/javascripts/jqtools/flashembed.js +300 -0
  9. data/app/assets/javascripts/jqtools/overlay.js +294 -0
  10. data/app/assets/javascripts/jqtools/scrollable.js +3 -0
  11. data/app/assets/javascripts/jqtools/scrollable/autoscroll.js +81 -0
  12. data/app/assets/javascripts/jqtools/scrollable/base.js +335 -0
  13. data/app/assets/javascripts/jqtools/scrollable/navigator.js +139 -0
  14. data/app/assets/javascripts/jqtools/validator.js +590 -0
  15. data/app/assets/javascripts/jqueryui/accordion.js +611 -0
  16. data/app/assets/javascripts/jqueryui/autocomplete.js +612 -0
  17. data/app/assets/javascripts/jqueryui/button.js +388 -0
  18. data/app/assets/javascripts/jqueryui/core.js +312 -0
  19. data/app/assets/javascripts/jqueryui/datepicker.js +1791 -0
  20. data/app/assets/javascripts/jqueryui/dialog.js +878 -0
  21. data/app/assets/javascripts/jqueryui/draggable.js +815 -0
  22. data/app/assets/javascripts/jqueryui/droppable.js +285 -0
  23. data/app/assets/javascripts/jqueryui/effects/blind.js +49 -0
  24. data/app/assets/javascripts/jqueryui/effects/bounce.js +78 -0
  25. data/app/assets/javascripts/jqueryui/effects/clip.js +54 -0
  26. data/app/assets/javascripts/jqueryui/effects/core.js +746 -0
  27. data/app/assets/javascripts/jqueryui/effects/drop.js +50 -0
  28. data/app/assets/javascripts/jqueryui/effects/explode.js +79 -0
  29. data/app/assets/javascripts/jqueryui/effects/fade.js +32 -0
  30. data/app/assets/javascripts/jqueryui/effects/fold.js +56 -0
  31. data/app/assets/javascripts/jqueryui/effects/highlight.js +50 -0
  32. data/app/assets/javascripts/jqueryui/effects/pulsate.js +51 -0
  33. data/app/assets/javascripts/jqueryui/effects/scale.js +178 -0
  34. data/app/assets/javascripts/jqueryui/effects/shake.js +57 -0
  35. data/app/assets/javascripts/jqueryui/effects/slide.js +50 -0
  36. data/app/assets/javascripts/jqueryui/effects/transfer.js +45 -0
  37. data/app/assets/javascripts/jqueryui/mouse.js +160 -0
  38. data/app/assets/javascripts/jqueryui/position.js +252 -0
  39. data/app/assets/javascripts/jqueryui/progressbar.js +109 -0
  40. data/app/assets/javascripts/jqueryui/resizable.js +814 -0
  41. data/app/assets/javascripts/jqueryui/selectable.js +266 -0
  42. data/app/assets/javascripts/jqueryui/slider.js +666 -0
  43. data/app/assets/javascripts/jqueryui/sortable.js +1077 -0
  44. data/app/assets/javascripts/jqueryui/tabs.js +758 -0
  45. data/app/assets/javascripts/jqueryui/widget.js +262 -0
  46. data/app/assets/javascripts/libs/backbone.js +1152 -0
  47. data/app/assets/javascripts/libs/cookie.js +89 -0
  48. data/app/assets/javascripts/libs/fileinput.js +130 -0
  49. data/app/assets/javascripts/libs/jplayer.js +1768 -0
  50. data/app/assets/javascripts/libs/proper.js +541 -0
  51. data/app/assets/javascripts/libs/sanitize.js +282 -0
  52. data/app/assets/javascripts/libs/selecttolist.js +75 -0
  53. data/app/assets/javascripts/libs/underscore.js +807 -0
  54. data/app/assets/javascripts/libs/uploadify.js +677 -0
  55. data/app/assets/javascripts/libs/wymeditor.js +9538 -0
  56. data/app/assets/javascripts/transit.js +4 -0
  57. data/app/assets/javascripts/transit/admin.js +22 -0
  58. data/app/assets/javascripts/transit/admin/contexts.js +52 -0
  59. data/app/assets/javascripts/transit/admin/fields.js +36 -0
  60. data/app/assets/javascripts/transit/admin/upload.js +109 -0
  61. data/app/assets/javascripts/transit/config.js.erb +101 -0
  62. data/app/assets/javascripts/transit/contexts/audio.js +39 -0
  63. data/app/assets/javascripts/transit/contexts/video.js +79 -0
  64. data/app/assets/javascripts/transit/core.js +171 -0
  65. data/app/assets/javascripts/transit/frontend.js +3 -0
  66. data/app/assets/javascripts/transit/lib/base64.js +120 -0
  67. data/app/assets/javascripts/transit/lib/editor.js +177 -0
  68. data/app/assets/javascripts/transit/views/audio_player.jst +22 -0
  69. data/app/assets/javascripts/transit/views/editor_toolbar.jst +12 -0
  70. data/app/assets/javascripts/transit/views/file_upload.jst +5 -0
  71. data/app/assets/javascripts/transit/views/video_player.jst +20 -0
  72. data/app/assets/javascripts/transit/views/wym_box.jst +4 -0
  73. data/app/assets/javascripts/transit/views/wym_iframe.jst +3 -0
  74. data/app/assets/stylesheets/transit.css.scss.erb +42 -0
  75. data/app/assets/stylesheets/transit/forms.css.scss +66 -0
  76. data/app/assets/stylesheets/transit/media/audio.css.scss +65 -0
  77. data/app/assets/stylesheets/transit/media/video.css.scss +30 -0
  78. data/app/assets/stylesheets/transit/panel.css.scss +100 -0
  79. data/app/assets/stylesheets/transit/ui.css.scss +507 -0
  80. data/app/controllers/pages_controller.rb +3 -0
  81. data/app/controllers/posts_controller.rb +3 -0
  82. data/app/controllers/transit/assets_controller.rb +38 -0
  83. data/app/controllers/transit/contexts_controller.rb +12 -9
  84. data/app/controllers/transit/pages_controller.rb +26 -0
  85. data/app/controllers/transit/posts_controller.rb +31 -0
  86. data/app/controllers/transit/topics_controller.rb +5 -0
  87. data/app/controllers/transit_controller.rb +16 -0
  88. data/app/helpers/transit/admin_helper.rb +43 -0
  89. data/app/helpers/transit/form_helper.rb +17 -0
  90. data/app/helpers/transit/package_helper.rb +41 -0
  91. data/app/helpers/transit/pagination_helper.rb +58 -0
  92. data/app/helpers/transit_helper.rb +42 -0
  93. data/app/models/comment.rb +37 -0
  94. data/app/models/contexts/audio.rb +12 -0
  95. data/app/models/{text.rb → contexts/text.rb} +0 -0
  96. data/app/models/contexts/video.rb +24 -0
  97. data/app/models/topic.rb +19 -0
  98. data/app/models/transit/asset.rb +68 -0
  99. data/{lib → app/models}/transit/context.rb +29 -0
  100. data/app/views/contexts/_text.html.erb +1 -1
  101. data/app/views/posts/index.rss.builder +18 -0
  102. data/app/views/transit/assets/_file.html.erb +4 -0
  103. data/app/views/transit/assets/_image.html.erb +13 -0
  104. data/app/views/transit/assets/create.js.erb +8 -0
  105. data/app/views/transit/assets/destroy.js.erb +3 -0
  106. data/app/views/transit/assets/manage.html.erb +20 -0
  107. data/app/views/transit/contexts/_audio.html.erb +18 -0
  108. data/app/views/transit/contexts/_text.html.erb +6 -0
  109. data/app/views/transit/contexts/_video.html.erb +13 -0
  110. data/app/views/transit/contexts/destroy.js.erb +1 -0
  111. data/app/views/transit/contexts/index.html.erb +5 -0
  112. data/app/views/transit/contexts/new.js.erb +7 -0
  113. data/app/views/transit/contexts/show.html.erb +8 -0
  114. data/app/views/transit/index.html.erb +26 -0
  115. data/app/views/transit/index.js.erb +1 -0
  116. data/app/views/transit/interface/post_panel.html.erb +96 -0
  117. data/app/views/transit/pages/_table.html.erb +7 -0
  118. data/app/views/transit/pages/edit.html.erb +17 -0
  119. data/app/views/transit/pages/index.html.erb +17 -0
  120. data/app/views/transit/pages/update.js.erb +1 -0
  121. data/app/views/transit/posts/_form.html.erb +49 -0
  122. data/app/views/transit/posts/edit.html.erb +14 -0
  123. data/app/views/transit/posts/new.html.erb +21 -0
  124. data/app/views/transit/table.html.erb +13 -0
  125. data/app/views/transit/table.js.erb +8 -0
  126. data/app/views/transit/topics/manage.html.erb +28 -0
  127. data/config/locales/en.yml +22 -0
  128. data/config/routes.rb +3 -3
  129. data/lib/transit.rb +51 -17
  130. data/lib/transit/admin.rb +85 -0
  131. data/lib/transit/builders/form_builder.rb +319 -0
  132. data/lib/transit/builders/jst_builder.rb +38 -0
  133. data/lib/transit/builders/package_builder.rb +45 -0
  134. data/lib/transit/config.rb +20 -0
  135. data/lib/transit/controller/generator.rb +42 -0
  136. data/lib/transit/controller/responder.rb +34 -0
  137. data/lib/transit/core_ext.rb +18 -0
  138. data/lib/transit/errors/resource_not_found.rb +6 -0
  139. data/lib/transit/model/assets.rb +14 -0
  140. data/lib/transit/model/attachments.rb +55 -0
  141. data/lib/transit/model/auto_increment.rb +22 -0
  142. data/lib/transit/model/base.rb +56 -0
  143. data/lib/transit/model/comments.rb +19 -0
  144. data/lib/transit/model/hooks.rb +38 -0
  145. data/lib/transit/model/owners.rb +14 -0
  146. data/lib/transit/model/paginator.rb +92 -0
  147. data/lib/transit/model/topics.rb +14 -0
  148. data/lib/transit/package/page.rb +20 -12
  149. data/lib/transit/package/post.rb +87 -33
  150. data/lib/transit/package/post/validations.rb +14 -0
  151. data/lib/transit/rails/engine.rb +29 -13
  152. data/lib/transit/rails/railtie.rb +31 -0
  153. data/lib/transit/rails/routing.rb +11 -6
  154. data/lib/transit/services.rb +13 -0
  155. data/lib/transit/services/base.rb +14 -0
  156. data/lib/transit/services/facebook.rb +13 -0
  157. data/lib/transit/services/ted.rb +10 -0
  158. data/lib/transit/services/twitter.rb +13 -0
  159. data/lib/transit/services/vimeo.rb +10 -0
  160. data/lib/transit/services/you_tube.rb +12 -0
  161. data/lib/transit/version.rb +1 -1
  162. metadata +178 -31
  163. data/app/assets/stylesheets/includes/_compat.scss +0 -24
  164. data/app/assets/stylesheets/includes/_defaults.scss +0 -99
  165. data/app/assets/stylesheets/includes/_global.scss +0 -16
  166. data/app/assets/stylesheets/includes/_imports.scss +0 -27
  167. data/app/assets/stylesheets/includes/_mixins.scss +0 -38
  168. data/app/assets/stylesheets/includes/_setup.scss +0 -85
  169. data/app/assets/stylesheets/layout.css.scss +0 -29
  170. data/app/assets/stylesheets/transit.css +0 -3
  171. data/app/controllers/application_controller.rb +0 -5
  172. data/app/controllers/transit/index_controller.rb +0 -7
  173. data/app/controllers/transit/packages_controller.rb +0 -64
  174. data/app/controllers/transit/transit_controller.rb +0 -4
  175. data/app/helpers/routing_helpers.rb +0 -7
  176. data/app/models/audio.rb +0 -4
  177. data/app/models/package_asset.rb +0 -11
  178. data/app/models/video.rb +0 -8
  179. data/app/views/contexts/_audio.html.erb +0 -1
  180. data/app/views/contexts/_video.html.erb +0 -1
  181. data/app/views/layouts/transit.html.erb +0 -31
  182. data/app/views/transit/index/index.html.erb +0 -0
  183. data/lib/transit/helpers/controller_helpers.rb +0 -40
  184. data/lib/transit/helpers/model_helpers.rb +0 -26
  185. data/lib/transit/package.rb +0 -25
  186. data/lib/transit/package/base.rb +0 -49
@@ -0,0 +1,541 @@
1
+ // (c) 2011 Michael Aufreiter
2
+ // Proper is freely distributable under the MIT license.
3
+ // For all details and documentation:
4
+ // http://github.com/michael/proper
5
+
6
+ (function(){
7
+
8
+ // _.Events (borrowed from Backbone.js)
9
+ // -----------------
10
+
11
+ // A module that can be mixed in to *any object* in order to provide it with
12
+ // custom events. You may `bind` or `unbind` a callback function to an event;
13
+ // `trigger`-ing an event fires all callbacks in succession.
14
+ //
15
+ // var object = {};
16
+ // _.extend(object, Backbone.Events);
17
+ // object.bind('expand', function(){ alert('expanded'); });
18
+ // object.trigger('expand');
19
+ //
20
+
21
+ _.Events = window.Backbone ? Backbone.Events : {
22
+
23
+ // Bind an event, specified by a string name, `ev`, to a `callback` function.
24
+ // Passing `"all"` will bind the callback to all events fired.
25
+ bind : function(ev, callback) {
26
+ var calls = this._callbacks || (this._callbacks = {});
27
+ var list = this._callbacks[ev] || (this._callbacks[ev] = []);
28
+ list.push(callback);
29
+ return this;
30
+ },
31
+
32
+ // Remove one or many callbacks. If `callback` is null, removes all
33
+ // callbacks for the event. If `ev` is null, removes all bound callbacks
34
+ // for all events.
35
+ unbind : function(ev, callback) {
36
+ var calls;
37
+ if (!ev) {
38
+ this._callbacks = {};
39
+ } else if (calls = this._callbacks) {
40
+ if (!callback) {
41
+ calls[ev] = [];
42
+ } else {
43
+ var list = calls[ev];
44
+ if (!list) return this;
45
+ for (var i = 0, l = list.length; i < l; i++) {
46
+ if (callback === list[i]) {
47
+ list.splice(i, 1);
48
+ break;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ return this;
54
+ },
55
+
56
+ // Trigger an event, firing all bound callbacks. Callbacks are passed the
57
+ // same arguments as `trigger` is, apart from the event name.
58
+ // Listening for `"all"` passes the true event name as the first argument.
59
+ trigger : function(ev) {
60
+ var list, calls, i, l;
61
+ if (!(calls = this._callbacks)) return this;
62
+ if (list = calls[ev]) {
63
+ for (i = 0, l = list.length; i < l; i++) {
64
+ list[i].apply(this, Array.prototype.slice.call(arguments, 1));
65
+ }
66
+ }
67
+ if (list = calls['all']) {
68
+ for (i = 0, l = list.length; i < l; i++) {
69
+ list[i].apply(this, arguments);
70
+ }
71
+ }
72
+ return this;
73
+ }
74
+ };
75
+
76
+ _.stripTags = function(input, allowed) {
77
+ // Strips HTML and PHP tags from a string
78
+ //
79
+ // version: 1009.2513
80
+ // discuss at: http://phpjs.org/functions/strip_tags
81
+ allowed = (((allowed || "") + "")
82
+ .toLowerCase()
83
+ .match(/<[a-z][a-z0-9]*>/g) || [])
84
+ .join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
85
+ var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
86
+ commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
87
+ return input.replace(commentsAndPhpTags, '').replace(tags, function($0, $1){
88
+ return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
89
+ });
90
+ };
91
+
92
+ // Initial Setup
93
+ // -------------
94
+
95
+ controlsTpl = ' \
96
+ <div class="proper-commands"> \
97
+ <a href="#" title="Emphasis (CTRL+SHIFT+E)" class="command em" command="em"><div>Emphasis</div></a> \
98
+ <a href="#" title="Strong (CTRL+SHIFT+S)" class="command strong" command="strong"><div>Strong</div></a> \
99
+ <!--<a href="#" title="Inline Code (CTRL+SHIFT+C)" class="command code" command="code"><div>Code</div></a>--> \
100
+ <a title="Link (CTRL+SHIFT+L)" href="#" class="command link" command="link"><div>Link</div></a>\
101
+ <a href="#" title="Bullet List (CTRL+SHIFT+B)" class="command ul" command="ul"><div>Bullets List</div></a>\
102
+ <a href="#" title="Numbered List (CTRL+SHIFT+N)" class="command ol" command="ol"><div>Numbered List</div></a>\
103
+ <a href="#" title="Indent (TAB)" class="command indent" command="indent"><div>Indent</div></a>\
104
+ <a href="#" title="Outdent (SHIFT+TAB)" class="command outdent" command="outdent"><div>Outdent</div></a>\
105
+ <br class="clear"/>\
106
+ </div>';
107
+
108
+ var COMMANDS = {
109
+ "em": "italic",
110
+ "strong": "bold",
111
+ "ul": "insertUnorderedList",
112
+ "ol": "insertOrderedList"
113
+ // "link": "createLink" // for some reason Firefox can't work with that
114
+ }
115
+
116
+ // Proper
117
+ // -----------
118
+
119
+ this.Proper = function(options) {
120
+ var activeElement = null, // element that's being edited
121
+ $controls,
122
+ self = {},
123
+ that = this,
124
+ pendingChange = false,
125
+ options = {},
126
+ defaultOptions = { // default options
127
+ multiline: true,
128
+ markup: true,
129
+ placeholder: 'Enter Text'
130
+ };
131
+
132
+ // Setup temporary hidden DOM Node, for sanitization
133
+ $('body').append($('<div id="proper_content"></div>').hide());
134
+ var rawContent = $('<div id="proper_raw_content" contenteditable="true"></div>');
135
+ rawContent.css('position', 'fixed');
136
+ rawContent.css('top', '20px');
137
+ rawContent.css('left', '20px');
138
+ rawContent.css('opacity', '0');
139
+
140
+ $('body').append(rawContent);
141
+
142
+ // Commands
143
+ // -----------
144
+
145
+ function tagActive(element) {
146
+ var sel = window.getSelection();
147
+ var range = sel.getRangeAt(0);
148
+ return range.startContainer.parentNode.localName === element || range.endContainer.parentNode.localName === element;
149
+ }
150
+
151
+ // A proper implementation of execCommand
152
+ function toggleTag(tag) {
153
+ var sel = window.getSelection();
154
+ var range = sel.getRangeAt(0);
155
+
156
+ if (sel+"".length == 0) return;
157
+
158
+ if (tagActive(tag)) {
159
+ document.execCommand('removeFormat', false, true);
160
+ } else {
161
+ var sel = window.getSelection();
162
+ var range = sel.getRangeAt(0);
163
+ document.execCommand('removeFormat', false, true);
164
+ document.execCommand('insertHTML', false, '<'+tag+'>'+window.getSelection()+'</'+tag+'>');
165
+ }
166
+ }
167
+
168
+ var commands = {
169
+ execEM: function() {
170
+ if (!document.queryCommandState('italic', false, true)) document.execCommand('removeFormat', false, true);
171
+ document.execCommand('italic', false, true);
172
+ return false;
173
+ },
174
+
175
+ execSTRONG: function() {
176
+ if (!document.queryCommandState('bold', false, true)) document.execCommand('removeFormat', false, true);
177
+ document.execCommand('bold', false, true);
178
+ return false;
179
+ },
180
+
181
+ execCODE: function() {
182
+ if (!tagActive('code')) document.execCommand('removeFormat', false, true);
183
+ toggleTag('code');
184
+ return false;
185
+ },
186
+
187
+ execUL: function() {
188
+ document.execCommand('insertUnorderedList', false, true);
189
+ return false;
190
+ },
191
+
192
+ execOL: function() {
193
+ document.execCommand('insertOrderedList', false, true);
194
+ return false;
195
+ },
196
+
197
+ execINDENT: function() {
198
+ if (document.queryCommandState('insertOrderedList', false, true) || document.queryCommandState('insertUnorderedList', false, true)) {
199
+ document.execCommand('indent', false, true);
200
+ }
201
+ return false;
202
+ },
203
+
204
+ execOUTDENT: function() {
205
+ if (document.queryCommandState('insertOrderedList', false, true) || document.queryCommandState('insertUnorderedList', false, true)) {
206
+ document.execCommand('outdent', false, true);
207
+ }
208
+ return false;
209
+ },
210
+
211
+ execLINK: function() {
212
+ document.execCommand('createLink', false, prompt('URL:'));
213
+ return false;
214
+ },
215
+
216
+ showHTML: function() {
217
+ alert($(this.el).html());
218
+ }
219
+ };
220
+
221
+ // TODO: enable proper sanitizing that allows markup to be pasted too
222
+ function sanitize() {
223
+ var rawContent = document.getElementById('proper_raw_content');
224
+ $('#proper_content').html($(rawContent).text());
225
+ }
226
+
227
+ function updateCommandState() {
228
+ if (!options.markup) return;
229
+ $(activeElement).focus();
230
+
231
+ $controls.find('.command').removeClass('selected');
232
+ _.each(COMMANDS, function(command, key) {
233
+ if (document.queryCommandState(command, false, true)) {
234
+ $controls.find('.command.'+key).addClass('selected');
235
+ }
236
+ if (tagActive('code')) {
237
+ $controls.find('.command.code').addClass('selected');
238
+ }
239
+ });
240
+ }
241
+
242
+ // Used for placeholders
243
+ function checkEmpty() {
244
+ if ($(activeElement).text().trim().length === 0) {
245
+ $(activeElement).addClass('empty');
246
+ if (options.markup) {
247
+ $(activeElement).html('<p>&laquo; '+options.placeholder+' &raquo;</p>');
248
+ } else {
249
+ $(activeElement).html('&laquo; '+options.placeholder+' &raquo;');
250
+ }
251
+ }
252
+
253
+ }
254
+
255
+ // Clean up the mess produced by contenteditable
256
+ function semantify(html) {
257
+ return html.replace(/<i>/g, '<em>')
258
+ .replace(/<\/i>/g, '</em>')
259
+ .replace(/<b>/g, '<strong>')
260
+ .replace(/<\/b>/g, '</strong>');
261
+ }
262
+
263
+ function saveSelection() {
264
+ if (window.getSelection) {
265
+ sel = window.getSelection();
266
+ if (sel.getRangeAt && sel.rangeCount) {
267
+ return sel.getRangeAt(0);
268
+ }
269
+ } else if (document.selection && document.selection.createRange) {
270
+ return document.selection.createRange();
271
+ }
272
+ return null;
273
+ }
274
+
275
+ function restoreSelection(range) {
276
+ if (range) {
277
+ if (window.getSelection) {
278
+ sel = window.getSelection();
279
+ sel.removeAllRanges();
280
+ sel.addRange(range);
281
+ } else if (document.selection && range.select) {
282
+ range.select();
283
+ }
284
+ }
285
+ }
286
+
287
+ function selectAll() {
288
+ range = document.createRange();
289
+ range.selectNodeContents($(activeElement)[0]);
290
+ selection = window.getSelection();
291
+ selection.removeAllRanges();
292
+ selection.addRange(range);
293
+ }
294
+
295
+ function bindEvents(el) {
296
+ $(el).unbind('paste');
297
+ $(el).unbind('keydown');
298
+ $(el).unbind('keyup');
299
+
300
+ $(el).bind('paste', function() {
301
+ var selection = saveSelection();
302
+ $('#proper_raw_content').focus();
303
+
304
+ // Immediately sanitize pasted content
305
+ setTimeout(function() {
306
+ sanitize();
307
+ restoreSelection(selection);
308
+ $(el).focus();
309
+
310
+ // Avoid nested paragraph correction resulting from paste
311
+ var content = $('#proper_content').html().trim();
312
+
313
+ // For some reason last </p> gets injected anyway
314
+ document.execCommand('insertHTML', false, content);
315
+ $('#proper_raw_content').html('');
316
+ }, 1);
317
+ });
318
+
319
+ // Prevent multiline
320
+ $(el).bind('keydown', function(e) {
321
+ if (!options.multiline && e.keyCode === 13) {
322
+ e.stopPropagation();
323
+ return false;
324
+ }
325
+
326
+ if (e.keyCode == 8 && $(activeElement).text().trim().length == 0) {
327
+ e.stopPropagation();
328
+ return false;
329
+ }
330
+ });
331
+
332
+ $(el).bind('blur', checkEmpty);
333
+ $(el).bind('click', updateCommandState);
334
+
335
+ $(el).bind('keyup', function(e) {
336
+ updateCommandState();
337
+ if ($(activeElement).text().trim().length > 0) {
338
+ $(activeElement).removeClass('empty');
339
+ } else {
340
+ // TODO: problematic when hitting enter on an empty div
341
+ selectAll();
342
+ document.execCommand('delete', false, "");
343
+ $(activeElement).addClass('empty');
344
+ }
345
+
346
+ // Trigger change events, but consolidate them to 200ms time slices
347
+ setTimeout(function() {
348
+ // Skip if there's already a change pending
349
+ if (!pendingChange) {
350
+ pendingChange = true;
351
+ setTimeout(function() {
352
+ pendingChange = false;
353
+ self.trigger('changed');
354
+ }, 200);
355
+ }
356
+ }, 10);
357
+ return true;
358
+ });
359
+ }
360
+
361
+ // Instance methods
362
+ // -----------
363
+
364
+ self.deactivate = function() {
365
+ $(activeElement).attr('contenteditable', 'false');
366
+ $(activeElement).unbind('paste');
367
+ $(activeElement).unbind('keydown');
368
+ $('.proper-commands').remove();
369
+ self.unbind('changed');
370
+ };
371
+
372
+ // Activate editor for a given element
373
+ self.activate = function(el, opts) {
374
+ options = {};
375
+ _.extend(options, defaultOptions, opts);
376
+
377
+ // Deactivate previously active element
378
+ self.deactivate();
379
+
380
+ // Make editable
381
+ $(el).attr('contenteditable', true);
382
+ activeElement = el;
383
+ bindEvents(el);
384
+
385
+ // Setup controls
386
+ if (options.markup) {
387
+ $controls = $(controlsTpl);
388
+ $controls.appendTo($(options.controlsTarget));
389
+ }
390
+
391
+ // Keyboard bindings
392
+ if (options.markup) {
393
+ $(activeElement).bind('keydown', 'ctrl+shift+e', commands.execEM);
394
+ $(activeElement).bind('keydown', 'ctrl+shift+s', commands.execSTRONG);
395
+ $(activeElement).bind('keydown', 'ctrl+shift+c', commands.execCODE);
396
+ $(activeElement).bind('keydown', 'ctrl+shift+l', commands.execLINK);
397
+ $(activeElement).bind('keydown', 'ctrl+shift+b', commands.execUL);
398
+ $(activeElement).bind('keydown', 'ctrl+shift+n', commands.execOL);
399
+ $(activeElement).bind('keydown', 'tab', commands.execINDENT);
400
+ $(activeElement).bind('keydown', 'shift+tab', commands.execOUTDENT);
401
+ }
402
+
403
+ updateCommandState();
404
+ if (el.hasClass('empty')) {
405
+ selectAll();
406
+ document.execCommand('delete', false, "");
407
+ }
408
+
409
+ $('.proper-commands a.command').click(function(e) {
410
+ commands['exec'+ $(e.currentTarget).attr('command').toUpperCase()]();
411
+ updateCommandState();
412
+ setTimeout(function() {
413
+ self.trigger('changed');
414
+ }, 10);
415
+ return false;
416
+ });
417
+ };
418
+
419
+ // Get current content
420
+ self.content = function() {
421
+ if ($(activeElement).hasClass('empty')) return '';
422
+
423
+ if (options.markup) {
424
+ return activeElement ? semantify($(activeElement).html()).trim() : '';
425
+ } else {
426
+ if (options.multiline) {
427
+ return _.stripTags($(activeElement).html().replace(/<div>/g, '\n')
428
+ .replace(/<\/div>/g, '')).trim();
429
+ } else {
430
+ return _.stripTags($(activeElement).html()).trim();
431
+ }
432
+ }
433
+ };
434
+
435
+ // Expose public API
436
+ // -----------
437
+
438
+ _.extend(self, _.Events);
439
+ return self;
440
+ };
441
+ })();
442
+
443
+ /*
444
+ * jQuery Hotkeys Plugin
445
+ * Copyright 2010, John Resig
446
+ * Dual licensed under the MIT or GPL Version 2 licenses.
447
+ *
448
+ * Based upon the plugin by Tzury Bar Yochay:
449
+ * http://github.com/tzuryby/hotkeys
450
+ *
451
+ * Original idea by:
452
+ * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
453
+ */
454
+
455
+ (function(jQuery){
456
+
457
+ jQuery.hotkeys = {
458
+ version: "0.8",
459
+
460
+ specialKeys: {
461
+ 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
462
+ 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
463
+ 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
464
+ 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
465
+ 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
466
+ 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
467
+ 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta"
468
+ },
469
+
470
+ shiftNums: {
471
+ "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
472
+ "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
473
+ ".": ">", "/": "?", "\\": "|"
474
+ }
475
+ };
476
+
477
+ function keyHandler( handleObj ) {
478
+ // Only care when a possible input has been specified
479
+ if ( typeof handleObj.data !== "string" ) {
480
+ return;
481
+ }
482
+
483
+ var origHandler = handleObj.handler,
484
+ keys = handleObj.data.toLowerCase().split(" ");
485
+
486
+ handleObj.handler = function( event ) {
487
+ // Don't fire in text-accepting inputs that we didn't directly bind to
488
+ if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
489
+ event.target.type === "text") ) {
490
+ return;
491
+ }
492
+
493
+ // Keypress represents characters, not special keys
494
+ var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
495
+ character = String.fromCharCode( event.which ).toLowerCase(),
496
+ key, modif = "", possible = {};
497
+
498
+ // check combinations (alt|ctrl|shift+anything)
499
+ if ( event.altKey && special !== "alt" ) {
500
+ modif += "alt+";
501
+ }
502
+
503
+ if ( event.ctrlKey && special !== "ctrl" ) {
504
+ modif += "ctrl+";
505
+ }
506
+
507
+ // TODO: Need to make sure this works consistently across platforms
508
+ if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
509
+ modif += "meta+";
510
+ }
511
+
512
+ if ( event.shiftKey && special !== "shift" ) {
513
+ modif += "shift+";
514
+ }
515
+
516
+ if ( special ) {
517
+ possible[ modif + special ] = true;
518
+
519
+ } else {
520
+ possible[ modif + character ] = true;
521
+ possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
522
+
523
+ // "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
524
+ if ( modif === "shift+" ) {
525
+ possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
526
+ }
527
+ }
528
+
529
+ for ( var i = 0, l = keys.length; i < l; i++ ) {
530
+ if ( possible[ keys[i] ] ) {
531
+ return origHandler.apply( this, arguments );
532
+ }
533
+ }
534
+ };
535
+ }
536
+
537
+ jQuery.each([ "keydown", "keyup", "keypress" ], function() {
538
+ jQuery.event.special[ this ] = { add: keyHandler };
539
+ });
540
+
541
+ })( jQuery );