yodel_admin 0.0.5 → 0.0.7

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.
@@ -53,7 +53,7 @@
53
53
  $('#save_failure').fadeIn().delay(2000).fadeOut();
54
54
  <% end %>
55
55
 
56
- <% record.field_sections.each do |section| %>
56
+ <% record.field_sections.values.each do |section| %>
57
57
  <% if section.display? %>
58
58
  <section>
59
59
  <% if section.name.present? %>
@@ -62,6 +62,7 @@
62
62
  <div <% if section.name.present? %> style="display: none" <% end %>>
63
63
  <%= form.form_for_section(section) %>
64
64
  </div>
65
+ <div style="clear: both"></div>
65
66
  </section>
66
67
  <% end %>
67
68
  <% end %>
@@ -45,6 +45,17 @@
45
45
  </header>
46
46
  <%= content %>
47
47
 
48
+
49
+ <div id="alert-background" style="display: none"></div>
50
+ <div id="alert" style="display: none">
51
+ <h1>Error</h1>
52
+ <p>Error text here</p>
53
+ <menu>
54
+ <a href="#" class="dismiss">Cancel</a>
55
+ <a href="#" class="confirm">OK</a>
56
+ </menu>
57
+ </div>
58
+
48
59
  <script src="/admin/js/jquery.tablesorter.min.js"></script>
49
60
  <script src="/admin/chosen/chosen.jquery.min.js"></script>
50
61
  <script src="/admin/js/jquery-ui-1.8.16.custom.min.js"></script>
@@ -12,7 +12,7 @@
12
12
  <h1>New</h1>
13
13
  <ul>
14
14
  <% site.models.each do |model| %>
15
- <% next if model.name.end_with?('_eigenmodel') || model.name.end_with?('Eigenmodel') %>
15
+ <% next if model.name.end_with?('_eigenmodel') || model.name.end_with?('Eigenmodel') || model.hide_in_admin %>
16
16
  <a href="#" class="model" data-type="<%= model.name %>" data-model-id="<%= model.id %>">
17
17
  <li>
18
18
  <img src="/admin/images/load_spinner.gif" class="spinner">
@@ -1,6 +1,7 @@
1
1
  <h1><span class="type"><%= @record.model_name.underscore.humanize.titleize %></span> <span id="record_name"><%= @record.name %></span></h1>
2
2
  <% form_for @record, page.path, remote: true, params: {id: 'record_form'} do |form| %>
3
3
  <% form.success do %>
4
+ // TODO: the form may need to be re-generated: the state of the form may not be the state that was submitted
4
5
  $('#save_success').fadeIn().delay(2000).fadeOut();
5
6
 
6
7
  // remove the model and parent id's regardless of whether this was a new or existing record
@@ -11,47 +12,82 @@
11
12
  $('#record_id').remove();
12
13
  $('#record_form').append('<input type="hidden" id="record_id" name="id" value="' + record.id + '">');
13
14
 
15
+ // update embedded record ids
16
+ var values = jQuery.parseJSON(record.values);
17
+ updateEmbeddedRecordIds(values);
18
+
19
+ // and image and attachment names
20
+ updateImageAndAttachmentNames(values);
21
+
14
22
  // further saves are updates
15
23
  $('#record_form input[name=_method]').val('put');
16
24
 
17
25
  // update the UI to reflect any changes in the record
18
26
  $('#record_name').html(record.name)
19
27
  updateRecord(record);
20
- var values = jQuery.parseJSON(record.values);
21
- if(values) {
22
- $('#record_form .field-type-attachment, #record_form .field-type-image').each(function(index, element) {
23
- element = jQuery(element);
24
- var field_name = element.attr('data-field');
25
-
26
- var file_name = 'none';
27
-
28
- if(values[field_name] && values[field_name].name) {
29
- file_name = values[field_name].name;
30
- element.find('img').attr('src', '/attachments/' + field_name + '/' + values._id + '/admin_thumb.jpg');
31
- element.find('img').show();
32
- element.find('.clear').show();
33
- element.find('input[type=file]').val('');
34
- } else {
35
- element.find('.clear').hide();
36
- }
37
- element.find('p span').html(file_name);
38
- });
39
- }
40
28
  <% end %>
41
29
 
42
30
  <% form.failure do %>
43
31
  $('#save_failure').fadeIn().delay(2000).fadeOut();
44
32
  <% end %>
45
33
 
46
- <% @record.field_sections.each do |section| %>
34
+ <% @record.field_sections.values.each do |section| %>
47
35
  <% if section.display? %>
36
+
48
37
  <section>
49
38
  <% if section.name.present? %>
50
39
  <h1><%= section.name %> <a href="#" class="section_toggle">show</a></h1>
51
40
  <% end %>
41
+
52
42
  <div <% if section.name.present? %> style="display: none" <% end %>>
53
- <%= form.form_for_section(section) %>
43
+ <% section.displayed_fields.each do |field| %>
44
+
45
+ <% if field.type == 'many_embedded' %>
46
+ <div
47
+ class='contains-field-type-<%= field.type %>'
48
+ data-field-human-name="<%= field.name.underscore.singularize.humanize.downcase %>"
49
+ data-field-name="<%= field.name %>">
50
+ <% if form.prefix %>
51
+ <input type="hidden" name="<%= form.prefix %>[][<%= field.name %>]" value="">
52
+ <% else %>
53
+ <input type="hidden" name="<%= field.name %>[]" value="">
54
+ <% end %>
55
+
56
+ <%= form.label(field.name) %>
57
+
58
+ <div>
59
+ <p class="no-embedded" <% unless @record.get(field.name).empty? %>style="display: none"<% end %>>
60
+ No <%= field.name.underscore.pluralize.humanize.downcase %>
61
+ </p>
62
+ <span class="field-type-many_embedded">
63
+ <% form.field(field.name, blank_record: true) do |embedded_form| %>
64
+ <% if embedded_form.record.new? %>
65
+ <span class="embedded-record blank-record">
66
+ <input type="hidden" name="<%= field.name %>[][_id]" value="">
67
+ <%= embedded_form.form_for_section(embedded_form.record.field_sections[nil]) %>
68
+ <% else %>
69
+ <span class="embedded-record" id="<%= embedded_form.record.id.to_s %>">
70
+ <input type="hidden" name="<%= field.name %>[][_id]" value="<%= embedded_form.record.id.to_s %>">
71
+ <%= embedded_form.form_for_section(embedded_form.record.field_sections[nil]) %>
72
+ <% end %>
73
+ <p class="delete-embedded"><a href="#">Delete</a></p>
74
+ <div style="clear: both"></div>
75
+ </span>
76
+ <% end %>
77
+ </span>
78
+
79
+ <%= form.status(field.name) %>
80
+ <p class="add-embedded"><a href="#">Add <%= field.name.underscore.singularize.humanize.downcase %></a></p>
81
+ </div>
82
+ </div>
83
+
84
+ <% else %>
85
+ <%= form.field_row(field.name, field) %>
86
+ <div style="clear: both"></div>
87
+ <% end %>
88
+ <% end %>
54
89
  </div>
90
+ <div style="clear: both"></div>
55
91
  </section>
56
92
  <% end %>
57
93
  <% end %>
@@ -170,6 +170,136 @@ header {
170
170
 
171
171
 
172
172
 
173
+ /* ---------------------------------------------------- */
174
+ /* alert dialog */
175
+ /* ---------------------------------------------------- */
176
+ #alert-background {
177
+ background-color: black;
178
+ position: fixed;
179
+ top: 0px;
180
+ left: 0px;
181
+ width: 100%;
182
+ height: 100%;
183
+
184
+ /* rgba is only supported by IE 9 */
185
+ opacity: 0.2;
186
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
187
+ filter: alpha(opacity=20);
188
+ }
189
+
190
+ #alert {
191
+ background-color: white;
192
+ border: 2px solid #191919;
193
+ -webkit-border-radius: 6px;
194
+ -moz-border-radius: 6px;
195
+ border-radius: 6px;
196
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
197
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
198
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
199
+
200
+ position: fixed;
201
+ width: 450px;
202
+ top: 50%;
203
+ left: 50%;
204
+ margin-left: -175px;
205
+ margin-top: -100px;
206
+ }
207
+
208
+ #alert h1 {
209
+ background-color: #2a2a2a;
210
+ padding: 12px 20px 14px 20px;
211
+ font-size: 18px;
212
+ margin-bottom: 10px;
213
+ color: #eee;
214
+
215
+ -webkit-border-top-left-radius: 3px;
216
+ -moz-border-radius-topleft: 3px;
217
+ border-top-left-radius: 3px;
218
+
219
+ -webkit-border-top-right-radius: 3px;
220
+ -moz-border-radius-topright: 3px;
221
+ border-top-right-radius: 3px;
222
+ }
223
+
224
+ #alert p {
225
+ padding: 20px 20px;
226
+ font-size: 15px;
227
+ line-height: 20px;
228
+ }
229
+
230
+ #alert menu {
231
+ background-color: #fdfdfd;
232
+ border-top: 1px solid #eee;
233
+ padding: 12px 17px;
234
+ text-align: right;
235
+ margin-top: 10px;
236
+
237
+ -webkit-border-bottom-left-radius: 3px;
238
+ -moz-border-radius-bottomleft: 3px;
239
+ border-bottom-left-radius: 3px;
240
+
241
+ -webkit-border-bottom-right-radius: 3px;
242
+ -moz-border-radius-bottomright: 3px;
243
+ border-bottom-right-radius: 3px;
244
+ }
245
+
246
+ #alert menu a {
247
+ cursor: pointer;
248
+ display: inline-block;
249
+ background-color: #e6e6e6;
250
+ background-repeat: no-repeat;
251
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));
252
+ background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
253
+ background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);
254
+ background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
255
+ background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
256
+ background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
257
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);
258
+ padding: 5px 14px 6px;
259
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
260
+ color: #333;
261
+ font-size: 13px;
262
+ line-height: normal;
263
+ border: 1px solid #ccc;
264
+ border-bottom-color: #bbb;
265
+ -webkit-border-radius: 4px;
266
+ -moz-border-radius: 4px;
267
+ border-radius: 4px;
268
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
269
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
270
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
271
+ -webkit-transition: 0.1s linear all;
272
+ -moz-transition: 0.1s linear all;
273
+ -ms-transition: 0.1s linear all;
274
+ -o-transition: 0.1s linear all;
275
+ transition: 0.1s linear all;
276
+ }
277
+
278
+ #alert menu a:active {
279
+ background-position: 0 -15px;
280
+ color: #333;
281
+ text-decoration: none;
282
+ }
283
+
284
+ #alert menu a.confirm {
285
+ margin-left: 10px;
286
+ color: #ffffff !important;
287
+ background-color: #0064cd;
288
+ background-repeat: repeat-x;
289
+ background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));
290
+ background-image: -moz-linear-gradient(top, #049cdb, #0064cd);
291
+ background-image: -ms-linear-gradient(top, #049cdb, #0064cd);
292
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));
293
+ background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
294
+ background-image: -o-linear-gradient(top, #049cdb, #0064cd);
295
+ background-image: linear-gradient(top, #049cdb, #0064cd);
296
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);
297
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
298
+ border-color: #0064cd #0064cd #003f81;
299
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
300
+ }
301
+
302
+
173
303
  /* ---------------------------------------------------- */
174
304
  /* main content containers */
175
305
  /* ---------------------------------------------------- */
@@ -300,12 +430,17 @@ article {
300
430
  float: left;
301
431
  }
302
432
 
303
- #record form > section > div > div > div {
433
+
434
+ #record form > section > div > div > div, /* top level, main record field */
435
+ .field-type-many_embedded > span > div > div, /* field of a many embedded record */
436
+ .field-type-one_embedded > span > div > div /* field of a single embedded record */
437
+ {
304
438
  padding-left: 110px;
305
439
  padding-right: 30px;
306
440
  vertical-align: top;
307
441
  }
308
442
 
443
+
309
444
  /* FIXME: html isn't added as a class to the div */
310
445
  #record form div.html {
311
446
  padding-right: 20px;
@@ -369,8 +504,13 @@ article {
369
504
  display: none;
370
505
  }
371
506
 
372
- .contains-field-type-image {
507
+ .contains-field-type-image, .contains-field-type-image + div {
373
508
  clear: both;
509
+ margin-top: 15px;
510
+ }
511
+
512
+ .contains-field-type-image {
513
+ min-height: 82px;
374
514
  }
375
515
 
376
516
  #record .image_preview {
@@ -427,6 +567,33 @@ article {
427
567
  width: auto;
428
568
  }
429
569
 
570
+ /* embedded fields */
571
+ .no-embedded, .add-embedded, .delete-embedded {
572
+ color: #777;
573
+ font-size: 13px;
574
+ }
575
+
576
+ .add-embedded, .delete-embedded {
577
+ text-align: right;
578
+ padding-right: 20px;
579
+ margin-top: 20px;
580
+ }
581
+
582
+ .add-embedded a, .delete-embedded a {
583
+ color: #777;
584
+ }
585
+
586
+ .delete-embedded {
587
+ clear: both;
588
+ }
589
+
590
+ .field-type-many_embedded > span {
591
+ display: block;
592
+ margin-bottom: 10px;
593
+ border-bottom: 1px solid #ccc;
594
+ padding-bottom: 10px;
595
+ }
596
+
430
597
  /* validations */
431
598
  .yodel-field-status {
432
599
  display: block;
@@ -11,11 +11,23 @@ function initialiseWidgets(newRecord) {
11
11
  hideModelPanel();
12
12
  initialiseHTMLFields();
13
13
  initialiseMultipleSelectFields();
14
+ initialiseEmbeddedFields();
14
15
  addClearLinks();
15
16
  if(newRecord)
16
17
  setDateTimeFieldsToNow();
17
18
  }
18
19
 
20
+ var blankRecords = {};
21
+ function initialiseEmbeddedFields() {
22
+ blankRecords = {};
23
+ $('.field-type-many_embedded .blank-record').each(function(index, record) {
24
+ var blankRecord = $(record);
25
+ blankRecord.hide();
26
+ blankRecord.removeClass('blank-record');
27
+ blankRecords[$(record).closest('.contains-field-type-many_embedded').attr('data-field-name')] = blankRecord.detach();
28
+ });
29
+ }
30
+
19
31
  function setDateTimeFieldsToNow() {
20
32
  // TODO: should check that each field is blank first before setting; a default value may have been set
21
33
  var now = new Date();
@@ -161,7 +173,7 @@ function constructRecordList(tree) {
161
173
  var navigationPanels = [];
162
174
 
163
175
  function dragError(parent, oldIndex, row) {
164
- alert('Sorry, an error occurred updating the position of this record. Please reload the page and try again.');
176
+ showAlert('Update error', 'Sorry, an error occurred updating the position of this record. Please reload the page and try again.');
165
177
 
166
178
  // move the row back to its old position; record indexes are 1 based
167
179
  if(oldIndex == 1) {
@@ -221,7 +233,7 @@ function pushNavigationPanel(rootName, tree, isPageRoot) {
221
233
  if(record && record.parent_id)
222
234
  parent = findRecord(tree, record.parent_id);
223
235
  if(!parent) {
224
- alert("Sorry, an error occurred updating the position of this record (the parent record could not be found). Please refresh the page and try again.");
236
+ showAlert('Update error', "Sorry, an error occurred updating the position of this record (the parent record could not be found). Please refresh the page and try again.");
225
237
  return;
226
238
  }
227
239
 
@@ -350,6 +362,57 @@ function hideChildren(event) {
350
362
  }
351
363
 
352
364
 
365
+ function updateEmbeddedRecordIds(record, embeddedIdElements) {
366
+ if(!embeddedIdElements)
367
+ embeddedIdElements = $.makeArray($('.embedded-record input[name*="_id"]'));
368
+
369
+ $.each(record, function(field, value) {
370
+ if(!value)
371
+ return;
372
+ if(value['_id']) {
373
+ $(embeddedIdElements.shift()).val(value['_id']);
374
+ } else if($.isArray(value)) {
375
+ updateEmbeddedRecordIds(value, embeddedIdElements);
376
+ }
377
+ });
378
+ }
379
+
380
+ function updateImageAndAttachmentNames(record, fieldElements, idStack) {
381
+ if(!fieldElements)
382
+ fieldElements = $.makeArray($('.field-type-attachment, .field-type-image'));
383
+ if(!idStack)
384
+ idStack = [record['_id']];
385
+
386
+ $.each(record, function(field, value) {
387
+ if(!value)
388
+ return;
389
+
390
+ if(!(value['name'] === undefined) && !(value['mime'] === undefined)) {
391
+ var field = $(fieldElements.shift());
392
+ if(value['name'] != null) {
393
+ var field_name = field.attr('data-field').replace(/\[\]/g, '').replace(/\[/g, '_').replace(/\]/g, '');
394
+ field.find('p span').html(value['name']);
395
+ field.find('img').attr('src', '/attachments/' + field_name + '/' + idStack.join('/') + '/admin_thumb.jpg');
396
+ field.find('img').show();
397
+ field.find('.clear').show();
398
+ field.find('input[type=file]').val('');
399
+ } else {
400
+ field.find('p span').html('none');
401
+ field.find('.clear').hide();
402
+ }
403
+
404
+ } else if(value['_id']) {
405
+ idStack.push(value['_id']);
406
+ updateImageAndAttachmentNames(value, fieldElements, idStack);
407
+ idStack.pop();
408
+
409
+ } else if($.isArray(value)) {
410
+ updateImageAndAttachmentNames(value, fieldElements, idStack);
411
+ }
412
+ });
413
+ }
414
+
415
+
353
416
  // update an existing record or add a record to the tree
354
417
  function updateRecord(record) {
355
418
  // either find the parent record by searching from the root of the
@@ -367,7 +430,7 @@ function updateRecord(record) {
367
430
  parent = findRecord(tree, record.parent_id);
368
431
 
369
432
  if(!treeRecord && !parent) {
370
- alert("Sorry, an error occurred while creating this record (no parent or record could be found). Please refresh the page and try again.");
433
+ showAlert('Create error', "Sorry, an error occurred while creating this record (no parent or record could be found). Please refresh the page and try again.");
371
434
  return;
372
435
  }
373
436
 
@@ -481,7 +544,9 @@ function deleteButtonPressed(event) {
481
544
  var recordTypeName = types[recordRow.attr('data-type')].humanName;
482
545
  var recordID = recordRow.attr('data-record-id');
483
546
 
484
- if(confirm('Are you sure you want to delete this ' + recordTypeName + '?')) {
547
+ showAlert('Confirm delete', 'Are you sure you want to delete this ' + recordTypeName + '?', function(ok) {
548
+ if(!ok)
549
+ return;
485
550
  showSpinner(recordRow);
486
551
  $.ajax(jsonURL, {
487
552
  data: {id: recordID, _method: 'delete'},
@@ -529,10 +594,10 @@ function deleteButtonPressed(event) {
529
594
  }, error: function(jqXHR, textStatus, errorThrown) {
530
595
  // FIXME: better error message
531
596
  hideSpinner(recordRow);
532
- alert('Sorry, an error occurred deleting this record. Please reload the page and try again.');
597
+ showAlert('Delete error', 'Sorry, an error occurred deleting this record. Please reload the page and try again.');
533
598
  }
534
599
  });
535
- }
600
+ });
536
601
  }
537
602
 
538
603
 
@@ -609,52 +674,58 @@ function toggleSectionVisibility(event) {
609
674
  }
610
675
 
611
676
 
612
- // delete an embedded doc
613
- function deleteEmbeddedDoc(event) {
677
+ // delete an embedded record
678
+ function deleteEmbeddedRecord(event) {
614
679
  event.preventDefault();
615
680
  var target = $(event.target);
616
- var documents = target.parents('div.embedded_documents');
617
- var type = documents.attr('data-type');
681
+ var fieldContainer = target.closest('div.contains-field-type-many_embedded');
682
+ var records = fieldContainer.find('.field-type-many_embedded');
683
+ var type = fieldContainer.attr('data-field-human-name');
618
684
 
619
- if(confirm('Are you sure you want to delete this ' + type + '?')) {
620
- target.parents('div.embedded_document').animate({
685
+ showAlert('Confirm delete', 'Are you sure you want to delete this ' + type + '?', function(ok) {
686
+ if(!ok)
687
+ return;
688
+
689
+ target.closest('.embedded-record').animate({
621
690
  height: 'hide',
622
691
  opacity: 'hide'
623
692
  }, {
624
693
  complete: function() {
625
694
  // show the 'No TYPE' text if we are removing the last embedded doc in a collection
626
- if(documents.find('div.embedded_document').size() == 1)
627
- documents.find('.no_embedded_documents').animate({height: 'show', opacity: 'show'});
695
+ if(records.find('> span').size() == 1)
696
+ fieldContainer.find('.no-embedded').animate({height: 'show', opacity: 'show'});
628
697
  $(this).remove();
629
698
  }
630
699
  });
631
- }
700
+ });
701
+
632
702
  }
633
703
 
634
- // add a new embedded doc
635
- function addEmbeddedDoc(event) {
704
+ // add a new embedded record
705
+ function addEmbeddedRecord(event) {
636
706
  event.preventDefault();
637
707
  var target = $(event.target);
638
- var documents = target.parents('div.embedded_documents');
708
+ var fieldContainer = target.closest('div');
709
+ var records = fieldContainer.find('.field-type-many_embedded');
710
+ var blankRecord = blankRecords[fieldContainer.closest('.contains-field-type-many_embedded').attr('data-field-name')];
639
711
 
640
- // construct a new document form an insert in to the collection
641
- var doc = $('<div class="embedded_document" style="display: none"><a href="#" class="delete_embedded_document"></a><table><tr><td id="label_col"></td><td id="input_col"></td></tr><tr><td><label>Photo</label></td><td><div class="attachment photo"><div class="photo_preview"><img src="sample_photo.jpg"></div><p class="current_value"><span class="title">File:</span>train.jpeg <a href="#" class="clear">Clear</a></p><p><span class="title">Select:</span><input type="file"></p><input type="hidden" name="delete_X" class="delete" value="false"></div></td></tr><tr><td><label>Name</label></td><td><input type="text"></td></tr></table></div>');
642
- $(event.target).before(doc);
712
+ var newRecord = blankRecord.clone();
713
+ $(records).append(newRecord);
643
714
 
644
- // hide the 'No TYPE' text if we are adding a new embedded doc in to an empty collection
645
- // animate the insertion of the new doc after hiding the 'No TYPE' text or immediately
646
- if(documents.find('div.embedded_document').size() == 1) {
647
- documents.find('.no_embedded_documents').animate({
715
+ //hide the 'No TYPE' text if we are adding a new embedded record in to an empty collection
716
+ // animate the insertion of the new record after hiding the 'No TYPE' text or immediately
717
+ if(records.find('> span').size() == 1) {
718
+ fieldContainer.find('.no-embedded').animate({
648
719
  height: 'hide',
649
720
  opacity: 'hide'
650
721
  }, {
651
722
  duration: 150,
652
723
  complete: function() {
653
- doc.animate({height: 'show', opacity: 'show'});
724
+ newRecord.animate({height: 'show', opacity: 'show'});
654
725
  }
655
726
  });
656
727
  } else {
657
- doc.animate({height: 'show', opacity: 'show'});
728
+ newRecord.animate({height: 'show', opacity: 'show'});
658
729
  }
659
730
  }
660
731
 
@@ -668,8 +739,8 @@ $('input[type=file]').live('change', hideClear);
668
739
 
669
740
  $('a.prev_nav_panel').live('click', showPreviousNavigationPanel);
670
741
  $('a.section_toggle').live('click', toggleSectionVisibility);
671
- $('a.delete_embedded_document').live('click', deleteEmbeddedDoc);
672
- $('a.add_embedded_document').live('click', addEmbeddedDoc);
742
+ $('.delete-embedded a').live('click', deleteEmbeddedRecord);
743
+ $('p.add-embedded a').live('click', addEmbeddedRecord);
673
744
 
674
745
  $('h1.new').live('click', showModelPanel);
675
746
  $('div.add_child').live('click', showModelPanel);
@@ -715,9 +786,16 @@ function highlightSelectedRow(row) {
715
786
  // load an existing record's form
716
787
  function loadRecord(id) {
717
788
  $('#record').load(htmlURL + '?action=show&id=' + id, function(text, status, req) {
718
- highlightSelectedRow(id);
719
- hideModelPanel();
720
- initialiseWidgets();
789
+ if(status == 'error') {
790
+ showAlert('Load error', 'Sorry, an error occurred loading this record. Please refresh the page and try again.');
791
+ } else {
792
+ highlightSelectedRow(id);
793
+ hideModelPanel();
794
+ initialiseWidgets();
795
+ setTimeout(function() {
796
+ $('#record').scrollTop(0);
797
+ }, 100);
798
+ }
721
799
  });
722
800
  }
723
801
 
@@ -752,11 +830,27 @@ function loadNewRecord(modelID, parentID, modelWasSelected) {
752
830
 
753
831
  // load the blank default record
754
832
  $('#record').load(htmlURL + '?action=new&model=' + modelID + '&parent=' + parentID, function(text, status, req) {
755
- initialiseWidgets(true);
756
- if(parent.children.length == 0)
757
- highlightSelectedRow(parentID);
758
- else
759
- highlightSelectedRow($('.record[data-record-id=' + parentID + '] > .child_elements > li > h1.new'));
833
+ if(status == 'error') {
834
+ // hide spinners from the navigation menu
835
+ if(parent.children.length == 0)
836
+ hideSpinner(parentID);
837
+ else
838
+ hideSpinner($('.record[data-record-id=' + parentID + '] > .child_elements > li > h1.new'));
839
+
840
+ // hide spinners from the new model panel
841
+ $('a.model .spinner').hide();
842
+ $('a.model .icon').show();
843
+
844
+ showAlert('Unable to load new record', 'Sorry, an error occurred loading a new record. Please refresh the page and try again.')
845
+
846
+ } else {
847
+ initialiseWidgets(true);
848
+ $('#record').scrollTop(0);
849
+ if(parent.children.length == 0)
850
+ highlightSelectedRow(parentID);
851
+ else
852
+ highlightSelectedRow($('.record[data-record-id=' + parentID + '] > .child_elements > li > h1.new'));
853
+ }
760
854
  });
761
855
  }
762
856
 
@@ -814,17 +908,17 @@ function push(creating) {
814
908
  jQuery.ajax(SYNC_URL, {type: 'POST', success: function(data) {
815
909
  if(!data.success) {
816
910
  if(data.reason)
817
- alert(errorText + ": " + data.reason);
911
+ showAlert('Push failed', errorText + ": " + data.reason);
818
912
  else if(data.conflicts)
819
- alert("The following files are in conflict and need to be resolved before pushing: " + data.conflicts.join(','));
913
+ showAlert('Push failed', "The following files are in conflict and need to be resolved before pushing: " + data.conflicts.join(','));
820
914
  else
821
- alert(errorText + ".");
915
+ showAlert('Push failed', errorText + ".");
822
916
  } else if(creating) {
823
917
  window.open('http://' + data.remoteDomain + '/', 'livesite');
824
918
  window.location.reload()
825
919
  }
826
920
  }, error: function(jqXHR, textStatus, errorThrown) {
827
- alert(errorText + ".");
921
+ showAlert('Push failed', errorText + ".");
828
922
  }, complete: function(jqXHR, textStatus) {
829
923
  hideSyncStatus();
830
924
  }});
@@ -849,11 +943,11 @@ $('.sync.create').live('click', function(event) {
849
943
  if(data.success) {
850
944
  push(true);
851
945
  } else {
852
- alert("Sorry, an error occurred while creating this site on the remote server: " + data.reason);
946
+ showAlert('Create failed', "Sorry, an error occurred while creating this site on the remote server: " + data.reason);
853
947
  hideSyncStatus();
854
948
  }
855
949
  }, error: function(jqXHR, textStatus, errorThrown) {
856
- alert("Sorry, an error occurred while creating this site on the remote server.");
950
+ showAlert('Create failed', "Sorry, an error occurred while creating this site on the remote server.");
857
951
  hideSyncStatus();
858
952
  }});
859
953
  });
@@ -869,16 +963,16 @@ $('.sync.pull').live('click', function(event) {
869
963
  jQuery.ajax(SYNC_URL, {type: 'GET', success: function(data) {
870
964
  if(!data.success) {
871
965
  if(data.reason)
872
- alert(errorText + ": " + data.reason);
966
+ showAlert('Pull failed', errorText + ": " + data.reason);
873
967
  else if(data.requires_push)
874
- alert(errorText + ": No remote server is associated with this site");
968
+ showAlert('Pull failed', errorText + ": No remote server is associated with this site");
875
969
  else if(data.conflicts)
876
- alert("The following files are in conflict and need to be resolved before pulling: " + data.conflicts.join(','));
970
+ showAlert('Pull failed', "The following files are in conflict and need to be resolved before pulling: " + data.conflicts.join(','));
877
971
  else
878
- alert(errorText + ".");
972
+ showAlert('Pull failed', errorText + ".");
879
973
  }
880
974
  }, error: function(jqXHR, textStatus, errorThrown) {
881
- alert(errorText + ".");
975
+ showAlert('Pull failed', errorText + ".");
882
976
  }, complete: function(jqXHR, textStatus) {
883
977
  hideSyncStatus();
884
978
  }});
@@ -960,7 +1054,7 @@ $('#list_items tbody tr').live('click', function() {
960
1054
  $('#record').load(htmlURL + '?id=' + recordID, function(text, status, req) {
961
1055
  $('#activity_spinner').fadeOut();
962
1056
  if(status == 'error') {
963
- alert("Sorry, an error occurred loading this record. Please refresh the page and try again.");
1057
+ showAlert('Load error', "Sorry, an error occurred loading this record. Please refresh the page and try again.");
964
1058
  } else {
965
1059
  highlightSelectedListRow(row);
966
1060
  initialiseWidgets();
@@ -1000,7 +1094,10 @@ $('.list #record .delete').live('click', function(event) {
1000
1094
  var recordID = $(event.target).attr('data-record-id');
1001
1095
  var recordTypeName = $(event.target).attr('data-record-type');
1002
1096
 
1003
- if(confirm('Are you sure you want to delete this ' + recordTypeName + '?')) {
1097
+ showAlert('Confirm delete', 'Are you sure you want to delete this ' + recordTypeName + '?', function(ok) {
1098
+ if(!ok)
1099
+ return;
1100
+
1004
1101
  $('#delete_spinner').fadeIn();
1005
1102
  $.ajax(jsonURL, {
1006
1103
  data: {id: recordID, _method: 'delete'},
@@ -1014,13 +1111,13 @@ $('.list #record .delete').live('click', function(event) {
1014
1111
 
1015
1112
  }, error: function(jqXHR, textStatus, errorThrown) {
1016
1113
  // FIXME: better error message
1017
- alert('Sorry, an error occurred deleting this record. Please reload the page and try again.');
1114
+ showAlert('Delete error', 'Sorry, an error occurred deleting this record. Please reload the page and try again.');
1018
1115
 
1019
1116
  }, complete: function() {
1020
1117
  $('#delete_spinner').fadeOut();
1021
1118
  }
1022
1119
  });
1023
- }
1120
+ });
1024
1121
  });
1025
1122
 
1026
1123
  $('#new_record').live('click', function(event) {
@@ -1031,7 +1128,7 @@ $('#new_record').live('click', function(event) {
1031
1128
  $('#record').load(htmlURL + '?new=true', function(text, status, req) {
1032
1129
  $('#activity_spinner').fadeOut();
1033
1130
  if(status == 'error') {
1034
- alert("Sorry, an error occurred loading a new record form. Please refresh the page and try again.");
1131
+ showAlert('Load error', "Sorry, an error occurred loading a new record. Please refresh the page and try again.");
1035
1132
  } else {
1036
1133
  highlightSelectedListRow();
1037
1134
  initialiseWidgets();
@@ -1040,3 +1137,84 @@ $('#new_record').live('click', function(event) {
1040
1137
  });
1041
1138
  })
1042
1139
 
1140
+
1141
+
1142
+
1143
+ /* alert modal dialog */
1144
+ var alertShowing = false;
1145
+ var confirmCallback = null;
1146
+
1147
+ // callback is null for simple 'OK' alerts; pass a
1148
+ // callback for a confirm ('Cancel', 'OK') alert
1149
+ function showAlert(title, text, callback) {
1150
+ // TODO: queue alerts in order
1151
+ if(alertShowing) {
1152
+ setTimeout(function() {
1153
+ showAlert(title, text, callback);
1154
+ }, 500);
1155
+ return;
1156
+ }
1157
+
1158
+ alertShowing = true;
1159
+ confirmCallback = callback;
1160
+ $('#alert h1').html(title);
1161
+ $('#alert p').html(text);
1162
+
1163
+ if(callback) {
1164
+ $('#alert menu a.confirm').show();
1165
+ $('#alert menu a.confirm').html('OK');
1166
+ $('#alert menu a.dismiss').html('Cancel');
1167
+ } else {
1168
+ $('#alert menu a.confirm').hide();
1169
+ $('#alert menu a.dismiss').html('OK');
1170
+ }
1171
+
1172
+ $('#alert-background').fadeIn('fast');
1173
+ $('#alert').fadeIn('fast');
1174
+ }
1175
+
1176
+ function hideAlert() {
1177
+ $('#alert-background').fadeOut('fast');
1178
+ $('#alert').fadeOut('fast');
1179
+ alertShowing = false;
1180
+ }
1181
+
1182
+ function cancelAlert() {
1183
+ if(confirmCallback)
1184
+ confirmCallback(false);
1185
+ confirmCallback = null;
1186
+ hideAlert();
1187
+ }
1188
+
1189
+ function confirmAlert() {
1190
+ if(confirmCallback)
1191
+ confirmCallback(true);
1192
+ confirmCallback = null;
1193
+ hideAlert();
1194
+ }
1195
+
1196
+ $('#alert menu a.dismiss').click(function(event) {
1197
+ event.preventDefault();
1198
+ cancelAlert();
1199
+ });
1200
+
1201
+ $('#alert menu a.confirm').click(function(event) {
1202
+ event.preventDefault();
1203
+ confirmAlert();
1204
+ });
1205
+
1206
+ $(document).keyup(function(event) {
1207
+ if(!alertShowing)
1208
+ return;
1209
+
1210
+ // enter/return
1211
+ if(event.which == 13)
1212
+ if(confirmCallback)
1213
+ confirmAlert();
1214
+ else
1215
+ cancelAlert();
1216
+
1217
+ // escape
1218
+ else if(event.which == 27)
1219
+ cancelAlert();
1220
+ });
@@ -1,3 +1,3 @@
1
1
  module YodelAdmin
2
- VERSION = '0.0.5'
2
+ VERSION = '0.0.7'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yodel_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-18 00:00:00.000000000Z
12
+ date: 2012-01-07 00:00:00.000000000Z
13
13
  dependencies: []
14
14
  description: Yodel CMS Admin Extension
15
15
  email: