transit 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +19 -11
- data/app/assets/images/transit/icon24x24.png +0 -0
- data/app/assets/images/transit/icon24x24_files.png +0 -0
- data/app/assets/images/transit/jplayer.swf +0 -0
- data/app/assets/images/transit/uploadify.swf +0 -0
- data/app/assets/images/transit/video_player.swf +0 -0
- data/app/assets/javascripts/jqtools/expose.js +224 -0
- data/app/assets/javascripts/jqtools/flashembed.js +300 -0
- data/app/assets/javascripts/jqtools/overlay.js +294 -0
- data/app/assets/javascripts/jqtools/scrollable.js +3 -0
- data/app/assets/javascripts/jqtools/scrollable/autoscroll.js +81 -0
- data/app/assets/javascripts/jqtools/scrollable/base.js +335 -0
- data/app/assets/javascripts/jqtools/scrollable/navigator.js +139 -0
- data/app/assets/javascripts/jqtools/validator.js +590 -0
- data/app/assets/javascripts/jqueryui/accordion.js +611 -0
- data/app/assets/javascripts/jqueryui/autocomplete.js +612 -0
- data/app/assets/javascripts/jqueryui/button.js +388 -0
- data/app/assets/javascripts/jqueryui/core.js +312 -0
- data/app/assets/javascripts/jqueryui/datepicker.js +1791 -0
- data/app/assets/javascripts/jqueryui/dialog.js +878 -0
- data/app/assets/javascripts/jqueryui/draggable.js +815 -0
- data/app/assets/javascripts/jqueryui/droppable.js +285 -0
- data/app/assets/javascripts/jqueryui/effects/blind.js +49 -0
- data/app/assets/javascripts/jqueryui/effects/bounce.js +78 -0
- data/app/assets/javascripts/jqueryui/effects/clip.js +54 -0
- data/app/assets/javascripts/jqueryui/effects/core.js +746 -0
- data/app/assets/javascripts/jqueryui/effects/drop.js +50 -0
- data/app/assets/javascripts/jqueryui/effects/explode.js +79 -0
- data/app/assets/javascripts/jqueryui/effects/fade.js +32 -0
- data/app/assets/javascripts/jqueryui/effects/fold.js +56 -0
- data/app/assets/javascripts/jqueryui/effects/highlight.js +50 -0
- data/app/assets/javascripts/jqueryui/effects/pulsate.js +51 -0
- data/app/assets/javascripts/jqueryui/effects/scale.js +178 -0
- data/app/assets/javascripts/jqueryui/effects/shake.js +57 -0
- data/app/assets/javascripts/jqueryui/effects/slide.js +50 -0
- data/app/assets/javascripts/jqueryui/effects/transfer.js +45 -0
- data/app/assets/javascripts/jqueryui/mouse.js +160 -0
- data/app/assets/javascripts/jqueryui/position.js +252 -0
- data/app/assets/javascripts/jqueryui/progressbar.js +109 -0
- data/app/assets/javascripts/jqueryui/resizable.js +814 -0
- data/app/assets/javascripts/jqueryui/selectable.js +266 -0
- data/app/assets/javascripts/jqueryui/slider.js +666 -0
- data/app/assets/javascripts/jqueryui/sortable.js +1077 -0
- data/app/assets/javascripts/jqueryui/tabs.js +758 -0
- data/app/assets/javascripts/jqueryui/widget.js +262 -0
- data/app/assets/javascripts/libs/backbone.js +1152 -0
- data/app/assets/javascripts/libs/cookie.js +89 -0
- data/app/assets/javascripts/libs/fileinput.js +130 -0
- data/app/assets/javascripts/libs/jplayer.js +1768 -0
- data/app/assets/javascripts/libs/proper.js +541 -0
- data/app/assets/javascripts/libs/sanitize.js +282 -0
- data/app/assets/javascripts/libs/selecttolist.js +75 -0
- data/app/assets/javascripts/libs/underscore.js +807 -0
- data/app/assets/javascripts/libs/uploadify.js +677 -0
- data/app/assets/javascripts/libs/wymeditor.js +9538 -0
- data/app/assets/javascripts/transit.js +4 -0
- data/app/assets/javascripts/transit/admin.js +22 -0
- data/app/assets/javascripts/transit/admin/contexts.js +52 -0
- data/app/assets/javascripts/transit/admin/fields.js +36 -0
- data/app/assets/javascripts/transit/admin/upload.js +109 -0
- data/app/assets/javascripts/transit/config.js.erb +101 -0
- data/app/assets/javascripts/transit/contexts/audio.js +39 -0
- data/app/assets/javascripts/transit/contexts/video.js +79 -0
- data/app/assets/javascripts/transit/core.js +171 -0
- data/app/assets/javascripts/transit/frontend.js +3 -0
- data/app/assets/javascripts/transit/lib/base64.js +120 -0
- data/app/assets/javascripts/transit/lib/editor.js +177 -0
- data/app/assets/javascripts/transit/views/audio_player.jst +22 -0
- data/app/assets/javascripts/transit/views/editor_toolbar.jst +12 -0
- data/app/assets/javascripts/transit/views/file_upload.jst +5 -0
- data/app/assets/javascripts/transit/views/video_player.jst +20 -0
- data/app/assets/javascripts/transit/views/wym_box.jst +4 -0
- data/app/assets/javascripts/transit/views/wym_iframe.jst +3 -0
- data/app/assets/stylesheets/transit.css.scss.erb +42 -0
- data/app/assets/stylesheets/transit/forms.css.scss +66 -0
- data/app/assets/stylesheets/transit/media/audio.css.scss +65 -0
- data/app/assets/stylesheets/transit/media/video.css.scss +30 -0
- data/app/assets/stylesheets/transit/panel.css.scss +100 -0
- data/app/assets/stylesheets/transit/ui.css.scss +507 -0
- data/app/controllers/pages_controller.rb +3 -0
- data/app/controllers/posts_controller.rb +3 -0
- data/app/controllers/transit/assets_controller.rb +38 -0
- data/app/controllers/transit/contexts_controller.rb +12 -9
- data/app/controllers/transit/pages_controller.rb +26 -0
- data/app/controllers/transit/posts_controller.rb +31 -0
- data/app/controllers/transit/topics_controller.rb +5 -0
- data/app/controllers/transit_controller.rb +16 -0
- data/app/helpers/transit/admin_helper.rb +43 -0
- data/app/helpers/transit/form_helper.rb +17 -0
- data/app/helpers/transit/package_helper.rb +41 -0
- data/app/helpers/transit/pagination_helper.rb +58 -0
- data/app/helpers/transit_helper.rb +42 -0
- data/app/models/comment.rb +37 -0
- data/app/models/contexts/audio.rb +12 -0
- data/app/models/{text.rb → contexts/text.rb} +0 -0
- data/app/models/contexts/video.rb +24 -0
- data/app/models/topic.rb +19 -0
- data/app/models/transit/asset.rb +68 -0
- data/{lib → app/models}/transit/context.rb +29 -0
- data/app/views/contexts/_text.html.erb +1 -1
- data/app/views/posts/index.rss.builder +18 -0
- data/app/views/transit/assets/_file.html.erb +4 -0
- data/app/views/transit/assets/_image.html.erb +13 -0
- data/app/views/transit/assets/create.js.erb +8 -0
- data/app/views/transit/assets/destroy.js.erb +3 -0
- data/app/views/transit/assets/manage.html.erb +20 -0
- data/app/views/transit/contexts/_audio.html.erb +18 -0
- data/app/views/transit/contexts/_text.html.erb +6 -0
- data/app/views/transit/contexts/_video.html.erb +13 -0
- data/app/views/transit/contexts/destroy.js.erb +1 -0
- data/app/views/transit/contexts/index.html.erb +5 -0
- data/app/views/transit/contexts/new.js.erb +7 -0
- data/app/views/transit/contexts/show.html.erb +8 -0
- data/app/views/transit/index.html.erb +26 -0
- data/app/views/transit/index.js.erb +1 -0
- data/app/views/transit/interface/post_panel.html.erb +96 -0
- data/app/views/transit/pages/_table.html.erb +7 -0
- data/app/views/transit/pages/edit.html.erb +17 -0
- data/app/views/transit/pages/index.html.erb +17 -0
- data/app/views/transit/pages/update.js.erb +1 -0
- data/app/views/transit/posts/_form.html.erb +49 -0
- data/app/views/transit/posts/edit.html.erb +14 -0
- data/app/views/transit/posts/new.html.erb +21 -0
- data/app/views/transit/table.html.erb +13 -0
- data/app/views/transit/table.js.erb +8 -0
- data/app/views/transit/topics/manage.html.erb +28 -0
- data/config/locales/en.yml +22 -0
- data/config/routes.rb +3 -3
- data/lib/transit.rb +51 -17
- data/lib/transit/admin.rb +85 -0
- data/lib/transit/builders/form_builder.rb +319 -0
- data/lib/transit/builders/jst_builder.rb +38 -0
- data/lib/transit/builders/package_builder.rb +45 -0
- data/lib/transit/config.rb +20 -0
- data/lib/transit/controller/generator.rb +42 -0
- data/lib/transit/controller/responder.rb +34 -0
- data/lib/transit/core_ext.rb +18 -0
- data/lib/transit/errors/resource_not_found.rb +6 -0
- data/lib/transit/model/assets.rb +14 -0
- data/lib/transit/model/attachments.rb +55 -0
- data/lib/transit/model/auto_increment.rb +22 -0
- data/lib/transit/model/base.rb +56 -0
- data/lib/transit/model/comments.rb +19 -0
- data/lib/transit/model/hooks.rb +38 -0
- data/lib/transit/model/owners.rb +14 -0
- data/lib/transit/model/paginator.rb +92 -0
- data/lib/transit/model/topics.rb +14 -0
- data/lib/transit/package/page.rb +20 -12
- data/lib/transit/package/post.rb +87 -33
- data/lib/transit/package/post/validations.rb +14 -0
- data/lib/transit/rails/engine.rb +29 -13
- data/lib/transit/rails/railtie.rb +31 -0
- data/lib/transit/rails/routing.rb +11 -6
- data/lib/transit/services.rb +13 -0
- data/lib/transit/services/base.rb +14 -0
- data/lib/transit/services/facebook.rb +13 -0
- data/lib/transit/services/ted.rb +10 -0
- data/lib/transit/services/twitter.rb +13 -0
- data/lib/transit/services/vimeo.rb +10 -0
- data/lib/transit/services/you_tube.rb +12 -0
- data/lib/transit/version.rb +1 -1
- metadata +178 -31
- data/app/assets/stylesheets/includes/_compat.scss +0 -24
- data/app/assets/stylesheets/includes/_defaults.scss +0 -99
- data/app/assets/stylesheets/includes/_global.scss +0 -16
- data/app/assets/stylesheets/includes/_imports.scss +0 -27
- data/app/assets/stylesheets/includes/_mixins.scss +0 -38
- data/app/assets/stylesheets/includes/_setup.scss +0 -85
- data/app/assets/stylesheets/layout.css.scss +0 -29
- data/app/assets/stylesheets/transit.css +0 -3
- data/app/controllers/application_controller.rb +0 -5
- data/app/controllers/transit/index_controller.rb +0 -7
- data/app/controllers/transit/packages_controller.rb +0 -64
- data/app/controllers/transit/transit_controller.rb +0 -4
- data/app/helpers/routing_helpers.rb +0 -7
- data/app/models/audio.rb +0 -4
- data/app/models/package_asset.rb +0 -11
- data/app/models/video.rb +0 -8
- data/app/views/contexts/_audio.html.erb +0 -1
- data/app/views/contexts/_video.html.erb +0 -1
- data/app/views/layouts/transit.html.erb +0 -31
- data/app/views/transit/index/index.html.erb +0 -0
- data/lib/transit/helpers/controller_helpers.rb +0 -40
- data/lib/transit/helpers/model_helpers.rb +0 -26
- data/lib/transit/package.rb +0 -25
- 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>« '+options.placeholder+' »</p>');
|
248
|
+
} else {
|
249
|
+
$(activeElement).html('« '+options.placeholder+' »');
|
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 );
|