transit 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 );