jipe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,393 @@
1
+ var Jipe = {};
2
+
3
+ Jipe.InPlaceEditor = Class.create();
4
+ Jipe.InPlaceEditor.defaultHighlightColor = "#FFFF99";
5
+ Jipe.InPlaceEditor.prototype = {
6
+ initialize: function(element, model, recordId, field, options) {
7
+ this.model = model;
8
+ this.recordId = recordId;
9
+ this.field = field;
10
+ this.element = $(element);
11
+
12
+ this.options = Object.extend({
13
+ paramName: "value",
14
+ okButton: true,
15
+ okText: "ok",
16
+ cancelLink: true,
17
+ cancelText: "cancel",
18
+ savingText: "Saving...",
19
+ clickToEditText: "Click to edit",
20
+ okText: "ok",
21
+ rows: 1,
22
+ onComplete: function(transport, element) {
23
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
24
+ },
25
+ onFailure: function(transport) {
26
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
27
+ },
28
+ callback: function(form) {
29
+ return Form.serialize(form);
30
+ },
31
+ handleLineBreaks: true,
32
+ loadingText: 'Loading...',
33
+ savingClassName: 'inplaceeditor-saving',
34
+ loadingClassName: 'inplaceeditor-loading',
35
+ formClassName: 'inplaceeditor-form',
36
+ highlightcolor: Jipe.InPlaceEditor.defaultHighlightColor,
37
+ highlightendcolor: "#FFFFFF",
38
+ externalControl: null,
39
+ submitOnBlur: false,
40
+ ajaxOptions: {},
41
+ evalScripts: false,
42
+ authenticityToken: null
43
+ }, options || {});
44
+
45
+ if(!this.options.formId && this.element.id) {
46
+ this.options.formId = this.element.id + "-inplaceeditor";
47
+ if ($(this.options.formId)) {
48
+ // there's already a form with that name, don't specify an id
49
+ this.options.formId = null;
50
+ }
51
+ }
52
+
53
+ if (this.options.externalControl) {
54
+ this.options.externalControl = $(this.options.externalControl);
55
+ }
56
+
57
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
58
+ if (!this.originalBackground) {
59
+ this.originalBackground = "transparent";
60
+ }
61
+
62
+ this.element.title = this.options.clickToEditText;
63
+
64
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
65
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
66
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
67
+ Event.observe(this.element, 'click', this.onclickListener);
68
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
69
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
70
+ if (this.options.externalControl) {
71
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
72
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
73
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
74
+ }
75
+ },
76
+ enterEditMode: function(evt) {
77
+ if (this.saving) return;
78
+ if (this.editing) return;
79
+ this.editing = true;
80
+ this.onEnterEditMode();
81
+ if (this.options.externalControl) {
82
+ Element.hide(this.options.externalControl);
83
+ }
84
+ Element.hide(this.element);
85
+ this.createForm();
86
+ this.element.parentNode.insertBefore(this.form, this.element);
87
+ // stop the event to avoid a page refresh in Safari
88
+ if (evt) {
89
+ Event.stop(evt);
90
+ }
91
+ return false;
92
+ },
93
+ createForm: function() {
94
+ this.form = document.createElement("form");
95
+ this.form.id = this.options.formId;
96
+ Element.addClassName(this.form, this.options.formClassName)
97
+ this.form.onsubmit = this.onSubmit.bind(this);
98
+
99
+ this.createEditField();
100
+
101
+ if (this.options.textarea) {
102
+ var br = document.createElement("br");
103
+ this.form.appendChild(br);
104
+ }
105
+
106
+ if (this.options.okButton) {
107
+ okButton = document.createElement("input");
108
+ okButton.type = "submit";
109
+ okButton.value = this.options.okText;
110
+ okButton.className = 'editor_ok_button';
111
+ this.form.appendChild(okButton);
112
+ }
113
+
114
+ if (this.options.cancelLink) {
115
+ cancelLink = document.createElement("a");
116
+ cancelLink.href = "#";
117
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
118
+ cancelLink.onclick = this.onclickCancel.bind(this);
119
+ cancelLink.className = 'editor_cancel';
120
+ this.form.appendChild(cancelLink);
121
+ }
122
+ },
123
+ hasHTMLLineBreaks: function(string) {
124
+ if (!this.options.handleLineBreaks) return false;
125
+ return string.match(/<br/i) || string.match(/<p>/i);
126
+ },
127
+ convertHTMLLineBreaks: function(string) {
128
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
129
+ },
130
+ createEditField: function() {
131
+ var text;
132
+ text = this.options.loadingText;
133
+
134
+ var obj = this;
135
+
136
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
137
+ this.options.textarea = false;
138
+ var textField = document.createElement("input");
139
+ textField.obj = this;
140
+ textField.type = "text";
141
+ textField.name = this.options.paramName;
142
+ textField.value = text;
143
+ textField.style.backgroundColor = this.options.highlightcolor;
144
+ textField.className = 'editor_field';
145
+ var size = this.options.size || this.options.cols || 0;
146
+ if (size != 0) textField.size = size;
147
+ if (this.options.submitOnBlur)
148
+ textField.onblur = this.onSubmit.bind(this);
149
+ this.editField = textField;
150
+ } else {
151
+ this.options.textarea = true;
152
+ var textArea = document.createElement("textarea");
153
+ textArea.obj = this;
154
+ textArea.name = this.options.paramName;
155
+ textArea.value = this.convertHTMLLineBreaks(text);
156
+ textArea.rows = this.options.rows;
157
+ textArea.cols = this.options.cols || 40;
158
+ textArea.className = 'editor_field';
159
+ if (this.options.submitOnBlur)
160
+ textArea.onblur = this.onSubmit.bind(this);
161
+ this.editField = textArea;
162
+ }
163
+
164
+ this.loadExternalText();
165
+ this.form.appendChild(this.editField);
166
+ },
167
+ getText: function() {
168
+ return this.element.innerHTML;
169
+ },
170
+ loadExternalText: function() {
171
+ Element.addClassName(this.form, this.options.loadingClassName);
172
+ this.editField.disabled = true;
173
+ this.model.find(this.recordId,
174
+ this.options.ajaxOptionsOnLoad || this.options.ajaxOptions,
175
+ this.onLoadedExternalText.bind(this));
176
+ },
177
+ onLoadedExternalText: function(obj) {
178
+ this.record = obj;
179
+ Element.removeClassName(this.form, this.options.loadingClassName);
180
+ this.editField.disabled = false;
181
+ this.editField.value = obj[this.field];
182
+ Field.scrollFreeActivate(this.editField);
183
+ },
184
+ onclickCancel: function() {
185
+ this.onComplete();
186
+ this.leaveEditMode();
187
+ return false;
188
+ },
189
+ onFailure: function(transport) {
190
+ this.options.onFailure(transport);
191
+ if (this.oldInnerHTML) {
192
+ this.element.innerHTML = this.oldInnerHTML;
193
+ this.oldInnerHTML = null;
194
+ }
195
+ return false;
196
+ },
197
+ onSubmit: function() {
198
+ // onLoading resets these so we need to save them away for the Ajax call
199
+ var form = this.form;
200
+ var value = this.editField.value;
201
+
202
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
203
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
204
+ // to be displayed indefinitely
205
+ this.onLoading();
206
+
207
+ this.record[this.field] = value;
208
+ extraUrlParams = {};
209
+ if (this.options.authenticityToken) {
210
+ extraUrlParams.authenticity_token = this.options.authenticityToken;
211
+ }
212
+
213
+ this.record.save(extraUrlParams, Object.extend({
214
+ // parameters: this.options.callback(form, value),
215
+ onComplete: this.onComplete.bind(this),
216
+ onFailure: this.onFailure.bind(this)
217
+ }, this.options.ajaxOptionsOnSubmit || this.options.ajaxOptions));
218
+
219
+ // stop the event to avoid a page refresh in Safari
220
+ if (arguments.length > 1) {
221
+ Event.stop(arguments[0]);
222
+ }
223
+ return false;
224
+ },
225
+ onLoading: function() {
226
+ this.saving = true;
227
+ this.removeForm();
228
+ this.leaveHover();
229
+ this.showSaving();
230
+ },
231
+ showSaving: function() {
232
+ this.oldInnerHTML = this.element.innerHTML;
233
+ this.element.innerHTML = this.options.savingText;
234
+ Element.addClassName(this.element, this.options.savingClassName);
235
+ this.element.style.backgroundColor = this.originalBackground;
236
+ Element.show(this.element);
237
+ },
238
+ removeForm: function() {
239
+ if(this.form) {
240
+ if (this.form.parentNode) Element.remove(this.form);
241
+ this.form = null;
242
+ }
243
+ },
244
+ enterHover: function() {
245
+ if (this.saving) return;
246
+ this.element.style.backgroundColor = this.options.highlightcolor;
247
+ if (this.effect) {
248
+ this.effect.cancel();
249
+ }
250
+ Element.addClassName(this.element, this.options.hoverClassName)
251
+ },
252
+ leaveHover: function() {
253
+ if (this.options.backgroundColor) {
254
+ this.element.style.backgroundColor = this.oldBackground;
255
+ }
256
+ Element.removeClassName(this.element, this.options.hoverClassName)
257
+ if (this.saving) return;
258
+ this.effect = new Effect.Highlight(this.element, {
259
+ startcolor: this.options.highlightcolor,
260
+ endcolor: this.options.highlightendcolor,
261
+ restorecolor: this.originalBackground
262
+ });
263
+ },
264
+ leaveEditMode: function() {
265
+ Element.removeClassName(this.element, this.options.savingClassName);
266
+ this.removeForm();
267
+ this.leaveHover();
268
+ this.element.style.backgroundColor = this.originalBackground;
269
+ Element.show(this.element);
270
+ if (this.options.externalControl) {
271
+ Element.show(this.options.externalControl);
272
+ }
273
+ this.editing = false;
274
+ this.saving = false;
275
+ this.element.innerHTML = this.record[this.field];
276
+ this.oldInnerHTML = null;
277
+ this.onLeaveEditMode();
278
+ },
279
+ onComplete: function(transport) {
280
+ this.leaveEditMode();
281
+ this.options.onComplete.bind(this)(transport, this.element);
282
+ },
283
+ onEnterEditMode: function() {},
284
+ onLeaveEditMode: function() {},
285
+ dispose: function() {
286
+ if (this.oldInnerHTML) {
287
+ this.element.innerHTML = this.oldInnerHTML;
288
+ }
289
+ this.leaveEditMode();
290
+ Event.stopObserving(this.element, 'click', this.onclickListener);
291
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
292
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
293
+ if (this.options.externalControl) {
294
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
295
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
296
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
297
+ }
298
+ }
299
+ };
300
+
301
+ Jipe.ImageToggle = Class.create();
302
+ Jipe.ImageToggle.defaultHighlightColor = "#FFFF99";
303
+ Jipe.ImageToggle.prototype = {
304
+ initialize: function(trueElement, falseElement, model, recordId, field, options) {
305
+ this.model = model;
306
+ this.recordId = recordId;
307
+ this.field = field;
308
+ this.trueElement = $(trueElement);
309
+ this.falseElement = $(falseElement);
310
+
311
+ this.options = Object.extend({
312
+ onComplete: function(transport, element) {
313
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
314
+ },
315
+ onFailure: function(transport) {
316
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
317
+ },
318
+ savingClassName: 'imagetoggle-saving',
319
+ loadingClassName: 'imagetoggle-loading',
320
+ highlightcolor: Jipe.ImageToggle.defaultHighlightColor,
321
+ highlightendcolor: "#FFFFFF",
322
+ ajaxOptions: {},
323
+ evalScripts: false,
324
+ authenticityToken: null
325
+ }, options || {});
326
+
327
+ this.originalBackground = Element.getStyle(this.trueElement, 'background-color');
328
+ if (!this.originalBackground) {
329
+ this.originalBackground = "transparent";
330
+ }
331
+
332
+ this.trueClickListener = this.setFalse.bindAsEventListener(this);
333
+ this.falseClickListener = this.setTrue.bindAsEventListener(this);
334
+ Event.observe(this.trueElement, 'click', this.trueClickListener);
335
+ Event.observe(this.falseElement, 'click', this.falseClickListener);
336
+
337
+ //this.loadExternalState();
338
+ this.controlEnabled = true;
339
+ },
340
+ loadExternalState: function() {
341
+ this.model.find(this.recordId,
342
+ this.options.ajaxOptionsOnLoad || this.options.ajaxOptions,
343
+ this.onLoadedExternalState.bind(this));
344
+ },
345
+ onLoadedExternalState: function(obj) {
346
+ this.record = obj;
347
+ this.updateImage(obj[this.field]);
348
+ },
349
+ updateImage: function(state) {
350
+ if (state == "1" || state == "true") {
351
+ this.trueElement.show();
352
+ this.falseElement.hide();
353
+ } else {
354
+ this.falseElement.show();
355
+ this.trueElement.hide();
356
+ }
357
+ },
358
+ onComplete: function(transport) {
359
+ this.controlEnabled = true;
360
+ this.options.onComplete.bind(this)();
361
+ },
362
+ setFalse: function() {
363
+ if (this.controlEnabled) {
364
+ this.setState(false);
365
+ }
366
+ },
367
+ setTrue: function() {
368
+ if (this.controlEnabled) {
369
+ this.setState(true);
370
+ }
371
+ },
372
+ setState: function(state) {
373
+ this.controlEnabled = false;
374
+ this.desiredState = state;
375
+ this.updateImage(this.desiredState);
376
+ this.model.find(this.recordId,
377
+ this.options.ajaxOptionsOnLoad || this.options.ajaxOptions,
378
+ this.setStateInner.bind(this));
379
+ },
380
+ setStateInner: function(record) {
381
+ this.record = record;
382
+ if (this.record[this.field] != this.desiredState) {
383
+ this.record[this.field] = this.desiredState;
384
+ extraUrlOptions = {};
385
+ if (this.options.authenticityToken) {
386
+ extraUrlOptions.authenticity_token = this.options.authenticityToken;
387
+ }
388
+ this.record.save(extraUrlOptions, this.onComplete.bind(this));
389
+ } else {
390
+ this.onComplete();
391
+ }
392
+ }
393
+ };
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Copies the JIPE resource files into the correct place in your Rails working directory.
3
+
4
+ Example:
5
+ ./script/generate jipe
6
+
7
+ This will create:
8
+ public/images/jipe/edit-field.png
@@ -0,0 +1,8 @@
1
+ class JipeGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.directory "public/images/jipe"
5
+ m.file 'edit-field.png', 'public/images/jipe/edit-field.png'
6
+ end
7
+ end
8
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'rails/init'
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,62 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{jipe}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Nat Budin"]
12
+ s.date = %q{2011-03-15}
13
+ s.email = %q{natbudin@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "README"
16
+ ]
17
+ s.files = [
18
+ "Gemfile",
19
+ "README",
20
+ "Rakefile",
21
+ "app/controllers/jipe_controller.rb",
22
+ "app/views/jipe/jester.js.erb",
23
+ "app/views/jipe/jipe.js.erb",
24
+ "assets/images/edit-field.png",
25
+ "assets/javascripts/jester.js",
26
+ "assets/javascripts/jipe.js",
27
+ "generators/jipe/USAGE",
28
+ "generators/jipe/jipe_generator.rb",
29
+ "generators/jipe/templates/edit-field.png",
30
+ "init.rb",
31
+ "install.rb",
32
+ "lib/jipe.rb",
33
+ "rails/init.rb",
34
+ "tasks/jipe_tasks.rake",
35
+ "test/jipe_test.rb",
36
+ "uninstall.rb"
37
+ ]
38
+ s.homepage = %q{http://github.com/nbudin/jipe}
39
+ s.licenses = ["MIT"]
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.5.0}
42
+ s.summary = %q{RESTful In-place editors for Rails using Jester}
43
+ s.test_files = [
44
+ "test/jipe_test.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
52
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
53
+ else
54
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
55
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
60
+ end
61
+ end
62
+