best_in_place 0.2.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/.travis.yml +5 -0
  2. data/Gemfile +1 -0
  3. data/best_in_place.gemspec +2 -1
  4. data/{test_app/public → lib/assets}/javascripts/best_in_place.js +1 -3
  5. data/{public → lib/assets}/javascripts/jquery.purr.js +0 -0
  6. data/lib/best_in_place.rb +1 -3
  7. data/lib/best_in_place/engine.rb +7 -0
  8. data/lib/best_in_place/helper.rb +1 -2
  9. data/lib/best_in_place/test_helpers.rb +8 -21
  10. data/lib/best_in_place/version.rb +1 -1
  11. data/spec/helpers/best_in_place_spec.rb +2 -12
  12. data/spec/integration/double_init_spec.rb +2 -2
  13. data/spec/integration/js_spec.rb +12 -12
  14. data/test_app/Gemfile +10 -3
  15. data/test_app/{public → app/assets}/images/red_pen.png +0 -0
  16. data/test_app/{public → app/assets}/javascripts/application.js +3 -2
  17. data/test_app/{public → app/assets}/stylesheets/.gitkeep +0 -0
  18. data/test_app/{public → app/assets}/stylesheets/scaffold.css +0 -0
  19. data/test_app/{public → app/assets}/stylesheets/style.css +0 -0
  20. data/test_app/app/views/layouts/application.html.erb +1 -1
  21. data/test_app/config/application.rb +12 -3
  22. metadata +32 -29
  23. data/lib/best_in_place/utils.rb +0 -15
  24. data/lib/generators/best_in_place/setup_generator.rb +0 -11
  25. data/public/javascripts/best_in_place.js +0 -481
  26. data/public/javascripts/jquery-1.4.4.js +0 -7179
  27. data/spec/integration/text_area_spec.rb +0 -30
  28. data/test_app/public/images/rails.png +0 -0
  29. data/test_app/public/javascripts/jquery-1.4.4.min.js +0 -167
  30. data/test_app/public/javascripts/jquery.purr.js +0 -161
  31. data/test_app/public/javascripts/rails.js +0 -148
@@ -1,15 +0,0 @@
1
- module BestInPlace
2
- class Utils
3
-
4
- def self.build_best_in_place_id(object, field)
5
- if object.is_a?(Symbol) || object.is_a?(String)
6
- return "best_in_place_#{object}_#{field}"
7
- end
8
-
9
- id = "best_in_place_#{object.class.to_s.demodulize.underscore}"
10
- id << "_#{object.id}" if object.class.ancestors.include?(ActiveRecord::Base)
11
- id << "_#{field}"
12
- id
13
- end
14
- end
15
- end
@@ -1,11 +0,0 @@
1
- module BestInPlace
2
-
3
- class SetupGenerator < Rails::Generators::Base
4
- source_root File.expand_path("../../../../public/javascripts", __FILE__)
5
- desc "Copies best_in_place.js to the /public/javascripts folder of your app."
6
-
7
- def copy_js
8
- copy_file "best_in_place.js", "public/javascripts/best_in_place.js"
9
- end
10
- end
11
- end
@@ -1,481 +0,0 @@
1
- /*
2
- BestInPlace (for jQuery)
3
- version: 0.1.0 (01/01/2011)
4
- @requires jQuery >= v1.4
5
- @requires jQuery.purr to display pop-up windows
6
-
7
- By Bernat Farrero based on the work of Jan Varwig.
8
- Examples at http://bernatfarrero.com
9
-
10
- Licensed under the MIT:
11
- http://www.opensource.org/licenses/mit-license.php
12
-
13
- Usage:
14
-
15
- Attention.
16
- The format of the JSON object given to the select inputs is the following:
17
- [["key", "value"],["key", "value"]]
18
- The format of the JSON object given to the checkbox inputs is the following:
19
- ["falseValue", "trueValue"]
20
- */
21
-
22
- function BestInPlaceEditor(e) {
23
- this.element = jQuery(e);
24
- this.initOptions();
25
- this.bindForm();
26
- this.initNil();
27
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
28
- }
29
-
30
- BestInPlaceEditor.prototype = {
31
- // Public Interface Functions //////////////////////////////////////////////
32
-
33
- activate : function() {
34
- var elem = this.isNil ? "" : this.element.html();
35
- this.oldValue = elem;
36
- $(this.activator).unbind("click", this.clickHandler);
37
- this.activateForm();
38
- },
39
-
40
- abort : function() {
41
- if (this.isNil) this.element.html(this.nil);
42
- else this.element.html(this.oldValue);
43
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
44
- },
45
-
46
- update : function() {
47
- var editor = this;
48
- if (this.formType in {"input":1, "textarea":1} && this.getValue() == this.oldValue)
49
- { // Avoid request if no change is made
50
- this.abort();
51
- return true;
52
- }
53
- this.isNil = false;
54
- editor.ajax({
55
- "type" : "post",
56
- "dataType" : "text",
57
- "data" : editor.requestData(),
58
- "success" : function(data){ editor.loadSuccessCallback(data); },
59
- "error" : function(request, error){ editor.loadErrorCallback(request, error); }
60
- });
61
- if (this.formType == "select") {
62
- var value = this.getValue();
63
- $.each(this.values, function(i, v) {
64
- if (value == v[0]) {
65
- editor.element.html(v[1]);
66
- }
67
- }
68
- );
69
- } else if (this.formType == "checkbox") {
70
- editor.element.html(this.getValue() ? this.values[1] : this.values[0]);
71
- } else {
72
- editor.element.html(this.getValue() != "" ? this.getValue() : this.nil);
73
- }
74
- },
75
-
76
- activateForm : function() {
77
- alert("The form was not properly initialized. activateForm is unbound");
78
- },
79
-
80
- // Helper Functions ////////////////////////////////////////////////////////
81
-
82
- initOptions : function() {
83
- // Try parent supplied info
84
- var self = this;
85
- self.element.parents().each(function(){
86
- self.url = self.url || jQuery(this).attr("data-url");
87
- self.collection = self.collection || jQuery(this).attr("data-collection");
88
- self.formType = self.formType || jQuery(this).attr("data-type");
89
- self.objectName = self.objectName || jQuery(this).attr("data-object");
90
- self.attributeName = self.attributeName || jQuery(this).attr("data-attribute");
91
- self.nil = self.nil || jQuery(this).attr("data-nil");
92
- self.inner_class = self.inner_class || jQuery(this).attr("data-inner-class");
93
- self.html_attrs = self.html_attrs || jQuery(this).attr("data-html-attrs");
94
- });
95
-
96
- // Try Rails-id based if parents did not explicitly supply something
97
- self.element.parents().each(function(){
98
- var res = this.id.match(/^(\w+)_(\d+)$/i);
99
- if (res) {
100
- self.objectName = self.objectName || res[1];
101
- }
102
- });
103
-
104
- // Load own attributes (overrides all others)
105
- self.url = self.element.attr("data-url") || self.url || document.location.pathname;
106
- self.collection = self.element.attr("data-collection") || self.collection;
107
- self.formType = self.element.attr("data-type") || self.formtype || "input";
108
- self.objectName = self.element.attr("data-object") || self.objectName;
109
- self.attributeName = self.element.attr("data-attribute") || self.attributeName;
110
- self.activator = self.element.attr("data-activator") || self.element;
111
- self.nil = self.element.attr("data-nil") || self.nil || "-";
112
- self.inner_class = self.element.attr("data-inner-class") || self.inner_class || null;
113
- self.html_attrs = self.element.attr("data-html-attrs") || self.html_attrs;
114
-
115
- if (!self.element.attr("data-sanitize")) {
116
- self.sanitize = true;
117
- }
118
- else {
119
- self.sanitize = (self.element.attr("data-sanitize") == "true");
120
- }
121
-
122
- if ((self.formType == "select" || self.formType == "checkbox") && self.collection !== null)
123
- {
124
- self.values = jQuery.parseJSON(self.collection);
125
- }
126
- },
127
-
128
- bindForm : function() {
129
- this.activateForm = BestInPlaceEditor.forms[this.formType].activateForm;
130
- this.getValue = BestInPlaceEditor.forms[this.formType].getValue;
131
- },
132
-
133
- initNil: function() {
134
- if (this.element.html() == "")
135
- {
136
- this.isNil = true
137
- this.element.html(this.nil)
138
- }
139
- },
140
-
141
- getValue : function() {
142
- alert("The form was not properly initialized. getValue is unbound");
143
- },
144
-
145
- // Trim and Strips HTML from text
146
- sanitizeValue : function(s) {
147
- if (this.sanitize)
148
- {
149
- var tmp = document.createElement("DIV");
150
- tmp.innerHTML = s;
151
- s = tmp.textContent || tmp.innerText;
152
- }
153
- return jQuery.trim(s);
154
- },
155
-
156
- /* Generate the data sent in the POST request */
157
- requestData : function() {
158
- // To prevent xss attacks, a csrf token must be defined as a meta attribute
159
- csrf_token = $('meta[name=csrf-token]').attr('content');
160
- csrf_param = $('meta[name=csrf-param]').attr('content');
161
-
162
- var data = "_method=put";
163
- data += "&" + this.objectName + '[' + this.attributeName + ']=' + encodeURIComponent(this.getValue());
164
-
165
- if (csrf_param !== undefined && csrf_token !== undefined) {
166
- data += "&" + csrf_param + "=" + encodeURIComponent(csrf_token);
167
- }
168
- return data;
169
- },
170
-
171
- ajax : function(options) {
172
- options.url = this.url;
173
- options.beforeSend = function(xhr){ xhr.setRequestHeader("Accept", "application/json"); };
174
- return jQuery.ajax(options);
175
- },
176
-
177
- // Handlers ////////////////////////////////////////////////////////////////
178
-
179
- loadSuccessCallback : function(data) {
180
- this.element.html(data[this.objectName]);
181
- this.element.trigger($.Event("ajax:success"), data);
182
-
183
- // Binding back after being clicked
184
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
185
- },
186
-
187
- loadErrorCallback : function(request, error) {
188
- this.element.html(this.oldValue);
189
-
190
- // Display all error messages from server side validation
191
- $.each(jQuery.parseJSON(request.responseText), function(index, value) {
192
- if( typeof(value) == "object") {value = index + " " + value.toString(); }
193
- var container = $("<span class='flash-error'></span>").html(value);
194
- container.purr();
195
- });
196
-
197
- // Binding back after being clicked
198
- $(this.activator).bind('click', {editor: this}, this.clickHandler);
199
- },
200
-
201
- clickHandler : function(event) {
202
- event.data.editor.activate();
203
- },
204
-
205
- setHtmlAttributes : function() {
206
- var formField = this.element.find(this.formType);
207
- var attrs = jQuery.parseJSON(this.html_attrs);
208
- for(var key in attrs){
209
- formField.attr(key, attrs[key]);
210
- }
211
- }
212
- };
213
-
214
-
215
- BestInPlaceEditor.forms = {
216
- "input" : {
217
- activateForm : function() {
218
- var output = '<form class="form_in_place" action="javascript:void(0)" style="display:inline;">';
219
- output += '<input type="text" name="'+ this.attributeName + '" value="' + this.sanitizeValue(this.oldValue) + '"';
220
- if (this.inner_class != null) {
221
- output += ' class="' + this.inner_class + '"';
222
- }
223
- output += '></form>'
224
- this.element.html(output);
225
- this.setHtmlAttributes();
226
- this.element.find('input')[0].select();
227
- this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler);
228
- this.element.find("input").bind('blur', {editor: this}, BestInPlaceEditor.forms.input.inputBlurHandler);
229
- this.element.find("input").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler);
230
- },
231
-
232
- getValue : function() {
233
- return this.sanitizeValue(this.element.find("input").val());
234
- },
235
-
236
- inputBlurHandler : function(event) {
237
- event.data.editor.update();
238
- },
239
-
240
- submitHandler : function(event) {
241
- event.data.editor.update();
242
- },
243
-
244
- keyupHandler : function(event) {
245
- if (event.keyCode == 27) {
246
- event.data.editor.abort();
247
- }
248
- }
249
- },
250
-
251
- "select" : {
252
- activateForm : function() {
253
- var output = "<form action='javascript:void(0)' style='display:inline;'><select>";
254
- var selected = "";
255
- var oldValue = this.oldValue;
256
- $.each(this.values, function(index, value) {
257
- selected = (value[1] == oldValue ? "selected='selected'" : "");
258
- output += "<option value='" + value[0] + "' " + selected + ">" + value[1] + "</option>";
259
- });
260
- output += "</select></form>";
261
- this.element.html(output);
262
- this.setHtmlAttributes();
263
- this.element.find("select").bind('change', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
264
- this.element.find("select").bind('blur', {editor: this}, BestInPlaceEditor.forms.select.blurHandler);
265
- this.element.find("select").bind('keyup', {editor: this}, BestInPlaceEditor.forms.select.keyupHandler);
266
- this.element.find("select")[0].focus();
267
- },
268
-
269
- getValue : function() {
270
- return this.sanitizeValue(this.element.find("select").val());
271
- },
272
-
273
- blurHandler : function(event) {
274
- event.data.editor.update();
275
- },
276
-
277
- keyupHandler : function(event) {
278
- if (event.keyCode == 27) event.data.editor.abort();
279
- }
280
- },
281
-
282
- "checkbox" : {
283
- activateForm : function() {
284
- var newValue = Boolean(this.oldValue != this.values[1]);
285
- var output = newValue ? this.values[1] : this.values[0];
286
- this.element.html(output);
287
- this.setHtmlAttributes();
288
- this.update();
289
- },
290
-
291
- getValue : function() {
292
- return Boolean(this.element.html() == this.values[1]);
293
- }
294
- },
295
-
296
- "textarea" : {
297
- activateForm : function() {
298
- // grab width and height of text
299
- width = this.element.css('width');
300
- height = this.element.css('height');
301
-
302
- // construct the form
303
- var output = '<form action="javascript:void(0)" style="display:inline;"><textarea>';
304
- output += this.sanitizeValue(this.oldValue);
305
- output += '</textarea></form>';
306
- this.element.html(output);
307
- this.setHtmlAttributes();
308
-
309
- // set width and height of textarea
310
- jQuery(this.element.find("textarea")[0]).css({ 'min-width': width, 'min-height': height });
311
- jQuery(this.element.find("textarea")[0]).elastic();
312
-
313
- this.element.find("textarea")[0].focus();
314
- this.element.find("textarea").bind('blur', {editor: this}, BestInPlaceEditor.forms.textarea.blurHandler);
315
- this.element.find("textarea").bind('keyup', {editor: this}, BestInPlaceEditor.forms.textarea.keyupHandler);
316
- },
317
-
318
- getValue : function() {
319
- return this.sanitizeValue(this.element.find("textarea").val());
320
- },
321
-
322
- blurHandler : function(event) {
323
- event.data.editor.update();
324
- },
325
-
326
- keyupHandler : function(event) {
327
- if (event.keyCode == 27) {
328
- BestInPlaceEditor.forms.textarea.abort(event.data.editor);
329
- }
330
- },
331
-
332
- abort : function(editor) {
333
- if (confirm("Are you sure you want to discard your changes?")) {
334
- editor.abort();
335
- }
336
- }
337
- }
338
- };
339
-
340
- jQuery.fn.best_in_place = function() {
341
- this.each(function(){
342
- if (!jQuery(this).data('bestInPlaceEditor')) {
343
- jQuery(this).data('bestInPlaceEditor', new BestInPlaceEditor(this));
344
- }
345
- });
346
- return this;
347
- };
348
-
349
-
350
-
351
- /**
352
- * @name Elastic
353
- * @descripton Elastic is Jquery plugin that grow and shrink your textareas automaticliy
354
- * @version 1.6.5
355
- * @requires Jquery 1.2.6+
356
- *
357
- * @author Jan Jarfalk
358
- * @author-email jan.jarfalk@unwrongest.com
359
- * @author-website http://www.unwrongest.com
360
- *
361
- * @licens MIT License - http://www.opensource.org/licenses/mit-license.php
362
- */
363
-
364
- (function(jQuery){
365
- jQuery.fn.extend({
366
- elastic: function() {
367
- // We will create a div clone of the textarea
368
- // by copying these attributes from the textarea to the div.
369
- var mimics = [
370
- 'paddingTop',
371
- 'paddingRight',
372
- 'paddingBottom',
373
- 'paddingLeft',
374
- 'fontSize',
375
- 'lineHeight',
376
- 'fontFamily',
377
- 'width',
378
- 'fontWeight'];
379
-
380
- return this.each( function() {
381
-
382
- // Elastic only works on textareas
383
- if ( this.type != 'textarea' ) {
384
- return false;
385
- }
386
-
387
- var $textarea = jQuery(this),
388
- $twin = jQuery('<div />').css({'position': 'absolute','display':'none','word-wrap':'break-word'}),
389
- lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
390
- minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
391
- maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
392
- goalheight = 0,
393
- i = 0;
394
-
395
- // Opera returns max-height of -1 if not set
396
- if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
397
-
398
- // Append the twin to the DOM
399
- // We are going to meassure the height of this, not the textarea.
400
- $twin.appendTo($textarea.parent());
401
-
402
- // Copy the essential styles (mimics) from the textarea to the twin
403
- var i = mimics.length;
404
- while(i--){
405
- $twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
406
- }
407
-
408
-
409
- // Sets a given height and overflow state on the textarea
410
- function setHeightAndOverflow(height, overflow){
411
- curratedHeight = Math.floor(parseInt(height,10));
412
- if($textarea.height() != curratedHeight){
413
- $textarea.css({'height': curratedHeight + 'px','overflow':overflow});
414
-
415
- }
416
- }
417
-
418
-
419
- // This function will update the height of the textarea if necessary
420
- function update() {
421
-
422
- // Get curated content from the textarea.
423
- var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ /g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
424
-
425
- // Compare curated content with curated twin.
426
- var twinContent = $twin.html().replace(/<br>/ig,'<br />');
427
-
428
- if(textareaContent+'&nbsp;' != twinContent){
429
-
430
- // Add an extra white space so new rows are added when you are at the end of a row.
431
- $twin.html(textareaContent+'&nbsp;');
432
-
433
- // Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
434
- if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
435
-
436
- var goalheight = $twin.height()+lineHeight;
437
- if(goalheight >= maxheight) {
438
- setHeightAndOverflow(maxheight,'auto');
439
- } else if(goalheight <= minheight) {
440
- setHeightAndOverflow(minheight,'hidden');
441
- } else {
442
- setHeightAndOverflow(goalheight,'hidden');
443
- }
444
-
445
- }
446
-
447
- }
448
-
449
- }
450
-
451
- // Hide scrollbars
452
- $textarea.css({'overflow':'hidden'});
453
-
454
- // Update textarea size on keyup, change, cut and paste
455
- $textarea.bind('keyup change cut paste', function(){
456
- update();
457
- });
458
-
459
- // Compact textarea on blur
460
- // Lets animate this....
461
- $textarea.bind('blur',function(){
462
- if($twin.height() < maxheight){
463
- if($twin.height() > minheight) {
464
- $textarea.height($twin.height());
465
- } else {
466
- $textarea.height(minheight);
467
- }
468
- }
469
- });
470
-
471
- // And this line is to catch the browser paste event
472
- $textarea.live('input paste',function(e){ setTimeout( update, 250); });
473
-
474
- // Run update once when elastic is initialized
475
- update();
476
-
477
- });
478
-
479
- }
480
- });
481
- })(jQuery);