documentation-editor 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 714e8b811361a5ba5c1bd64cd4d8e0979c83bbd3
4
- data.tar.gz: 0980c654431f99c7f1876bbfff26ecda869c2127
3
+ metadata.gz: 5c1b887bb5ba7cc9014c287d2aa2ed28453c53f4
4
+ data.tar.gz: b7d916b3011f1837912961c63f48703189fe45c8
5
5
  SHA512:
6
- metadata.gz: 3a1583330738b2372e5ab055078e8f752ea4fe7166f61f2de1ade9b7bc2b8137e83236c8d79524be554d6c8441a05af93dd6f6b1bd7b12cca7deeb679689216b
7
- data.tar.gz: 01cf954b93b72676dcbc105309222e6277cce5e48075c2b03b197b24c9421b4d4f0f3cbffee38f93cf6b758cc9d8d41ea52c503a129c2bfd885865b5b035cd65
6
+ metadata.gz: 0a2f8125e3fc22d8af168e03d10ed5244e2e309524b0fc45cb254efebf04e55ede5a88934ccf193738607e882de6eb3ae9d7af7184400da17d3bdc07e31960c1
7
+ data.tar.gz: 4d3d7f39146758c4e62cd2bde32cfc6cff878d87ef69bf1ab4a3fa7a97c8c4bfbf62a763653ca4b480e76da64baaa9945c4d4bae01cf79d16e84f7163dd3ebb7
data/README.md CHANGED
@@ -9,12 +9,13 @@ The goal of this project is to provide an easy & frictionless way to edit an onl
9
9
  ## Features
10
10
 
11
11
  - Widgets
12
- - Text: Full markdown support
12
+ - Text: full markdown support
13
13
  - Callout: info/warning/danger
14
14
  - Tables: customizable number of columns & rows
15
15
  - Code: code snippet with highlighting
16
16
  - Conditions: ability to display some sections based on some query parameters
17
17
  - Image: image uploader + ability to store on S3
18
+ - Buttons: labeled buttons
18
19
  - Caching
19
20
  - Automatic TOC generation (anchors are automatically generated on each title)
20
21
  - Raw edition mode
@@ -61,18 +61,20 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
61
61
  data.push(section.content + "\n");
62
62
  } else {
63
63
  data.push('[block:' + section.type + ']');
64
- data.push(JSON.stringify(section.content));
64
+ data.push(JSON.stringify(section.content, null, 2));
65
65
  data.push("[/block]\n");
66
66
  }
67
67
  });
68
68
  return data.join("\n");
69
69
  }
70
70
 
71
- $scope.init = function(id, slug, thumbnailUrl, path) {
71
+ $scope.init = function(id, slug, thumbnailUrl, path, published_revision_id, last_revision_id) {
72
72
  $scope.id = id;
73
73
  $scope.slug = slug;
74
74
  $scope.path = path;
75
75
  $scope.thumbnailUrl = thumbnailUrl;
76
+ $scope.published_revision_id = published_revision_id;
77
+ $scope.last_revision_id = last_revision_id;
76
78
 
77
79
  $http.get($scope.path + '/admin/' + id).then(function(content) {
78
80
  $scope.source = content.data.source;
@@ -102,6 +104,9 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
102
104
  $scope.sections = $.grep($scope.sections, function(e) { return e.id !== o.data.obj.id });
103
105
  } else if (o.type === 'deleteSection') {
104
106
  $scope.sections = o.data.sections;
107
+ } else if (o.type === 'text') {
108
+ o.model.$setViewValue(o.data);
109
+ o.model.$render();
105
110
  } else if (o.type === 'deleteLanguage' || o.type === 'addLanguage') {
106
111
  var index = getIndex(o.data.id);
107
112
  var obj = {
@@ -112,6 +117,16 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
112
117
  }
113
118
  };
114
119
  $scope.sections = [].concat($scope.sections.slice(0, index), [obj], $scope.sections.slice(index + 1));
120
+ } else if (o.type === 'deleteButton' || o.type === 'addButton') {
121
+ var index = getIndex(o.data.id);
122
+ var obj = {
123
+ id: $scope.sections[index].id,
124
+ type: 'buttonse',
125
+ content: {
126
+ buttons: o.data.buttons
127
+ }
128
+ };
129
+ $scope.sections = [].concat($scope.sections.slice(0, index), [obj], $scope.sections.slice(index + 1));
115
130
  } else if (o.type === 'deleteColumn' || o.type === 'addColumn' || o.type === 'addRow' || o.type === 'deleteRow') {
116
131
  var index = getIndex(o.data.id);
117
132
  $scope.sections[index] = {
@@ -218,6 +233,22 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
218
233
  });
219
234
  };
220
235
 
236
+ $scope.addButtons = function($event, id) {
237
+ $event.preventDefault();
238
+ add(id, {
239
+ id: getNextID(),
240
+ type: 'buttons',
241
+ content: {
242
+ buttons: [
243
+ {
244
+ label: 'FIXME',
245
+ link: 'http://example.org'
246
+ }
247
+ ]
248
+ }
249
+ });
250
+ };
251
+
221
252
  $scope.getIntegerIterator = function(v) {
222
253
  var a = [];
223
254
  for (var i = 0; i < v; ++i) {
@@ -263,15 +294,42 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
263
294
  $scope.sections[index].content.codes = [].concat($scope.sections[index].content.codes, [{ language: 'FIXME:language|label', code: '// FIXME' }]);
264
295
  };
265
296
 
297
+ $scope.deleteButton = function($event, id, buttonIndex) {
298
+ $event.preventDefault();
299
+ var index = getIndex(id);
300
+ $scope.undoRedo.push({
301
+ type: 'deleteButton',
302
+ data: {
303
+ id: id,
304
+ buttons: $scope.sections[index].content.buttons
305
+ }
306
+ });
307
+ $scope.sections[index].content.buttons = $.grep($scope.sections[index].content.buttons, function(e, i) { return i !== buttonIndex; });
308
+ };
309
+
310
+ $scope.addButton = function($event, id) {
311
+ $event.preventDefault();
312
+ var index = getIndex(id);
313
+ $scope.undoRedo.push({
314
+ type: 'addButton',
315
+ data: {
316
+ id: id,
317
+ codes: $scope.sections[index].content.buttons
318
+ }
319
+ });
320
+ $scope.sections[index].content.buttons = [].concat($scope.sections[index].content.buttons, [{ label: 'FIXME', link: 'https://example.org' }]);
321
+ };
322
+
266
323
  $scope.imageToAddAfter = 0;
267
324
  $scope.saveImagePosition = function(id) {
268
325
  $scope.imageToAddAfter = id;
269
326
  };
270
- $scope.addImage = function(url) {
327
+ $scope.addImage = function(url, caption) {
271
328
  $('#editor-images').modal('hide');
272
329
  add($scope.imageToAddAfter, {
273
330
  type: 'image',
274
331
  content: {
332
+ caption: caption,
275
333
  images: [
276
334
  {
277
335
  image: [url]
@@ -370,6 +428,17 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
370
428
  section.content.rows = section.content.rows - 1;
371
429
  };
372
430
 
431
+ $scope.addHTML = function($event, id) {
432
+ $event.preventDefault();
433
+ add(id, {
434
+ id: getNextID(),
435
+ type: 'html',
436
+ content: {
437
+ body: '<span>FIXME</span>'
438
+ }
439
+ });
440
+ };
441
+
373
442
  $scope.addIf = function($event, id) {
374
443
  $event.preventDefault();
375
444
  add(id, {
@@ -421,8 +490,17 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
421
490
  });
422
491
  };
423
492
 
493
+ $scope.diff = function() {
494
+ return $scope.undoRedo.length > 0 && !$scope.undoRedo[$scope.undoRedo.length - 1].saved;
495
+ };
496
+
424
497
  function save(preview) {
425
- return $http.post($scope.path + '/admin/' + $scope.id, { data: serialize($scope.sections), preview: preview });
498
+ return $http.post($scope.path + '/admin/' + $scope.id, { data: serialize($scope.sections), preview: preview }).then(function(content) {
499
+ if ($scope.undoRedo.length > 0) {
500
+ $scope.undoRedo[$scope.undoRedo.length - 1].saved = true;
501
+ }
502
+ $scope.last_revision_id = content.data.id;
503
+ });
426
504
  }
427
505
 
428
506
  $scope.save = function($event) {
@@ -464,7 +542,7 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
464
542
  file: file
465
543
  }).success(function (data, status, headers, config) {
466
544
  console.log('uploaded', data);
467
- $scope.$parent.addImage(data.url);
545
+ $scope.$parent.addImage(data.url, data.caption);
468
546
  }).error(function (data, status, headers, config) {
469
547
  console.log('error', data);
470
548
  alert('Error status: ' + status);
@@ -520,31 +598,54 @@ angular.module('documentationEditorApp', ['ngFileUpload'])
520
598
  });
521
599
  });
522
600
  }]).directive('contenteditable', ['$document', function($document) {
601
+ var before = '';
602
+
523
603
  return {
524
604
  require: 'ngModel',
525
605
  link: function(scope, element, attrs, ngModel) {
526
- function read() {
606
+ function update(undoable) {
527
607
  // use the HTML form to keep the new lines
528
608
  var text = element.html();
529
609
  // replace the HTML newlines by \n
530
610
  text = text.replace(/<div>/g, '<br>').replace(/<\/div>/g, '').replace(/<br>/g, "\n");
531
- // replace also all HTML entities
532
- text = $('<div />').html(text).text();
533
- ngModel.$setViewValue(text);
611
+ if (undoable) {
612
+ scope.undoRedo.push({
613
+ type: 'text',
614
+ model: ngModel,
615
+ data: before
616
+ });
617
+ ngModel.$setViewValue(text);
618
+ }
534
619
  }
535
620
 
536
621
  ngModel.$render = function() {
537
- element.text(ngModel.$viewValue || "");
622
+ var val = ngModel.$viewValue || '';
623
+ if (val.indexOf('<') !== -1) {
624
+ // backward compat: before 2015/11/9 the values were not escaped
625
+ // consider them as text values
626
+ element.text(val);
627
+ } else {
628
+ element.html(val);
629
+ }
538
630
  };
539
631
 
540
- element.bind("blur keyup change", function() {
541
- scope.$apply(read);
632
+ element.bind("focus", function() {
633
+ before = ngModel.$viewValue;
634
+ });
635
+
636
+ element.bind("blur", function() {
637
+ scope.$apply(update.bind(null, true));
638
+ });
639
+
640
+ element.bind("keyup change", function() {
641
+ scope.$apply(update.bind(null, false));
542
642
  });
543
643
 
544
644
  // force every copy/paste to be plain/text
545
645
  element.bind('paste', function(e) {
546
646
  e.preventDefault();
547
647
  var text = (e.originalEvent || e).clipboardData.getData('text/plain') || '';
648
+ text = $('<div />').text(text).html(); // escape HTML entities, we're dealing with plain text here
548
649
  $document[0].execCommand("insertHTML", false, text);
549
650
  });
550
651
  }
@@ -113,6 +113,11 @@
113
113
  border: 1px solid #DDD
114
114
  margin-right: 5px
115
115
 
116
+ .history
117
+ tr.published
118
+ background: lighten(#58B957, 20%)
119
+ color: white
120
+
116
121
  .markdown-page
117
122
  h1, h2, h3, h4, h5, h6
118
123
  position: relative
@@ -39,8 +39,8 @@ module DocumentationEditor
39
39
  end
40
40
 
41
41
  def commit
42
- @page.add_revision!(params[:data], params[:preview].to_s == 'false', respond_to?(:current_user) ? current_user.id : nil)
43
- render nothing: true
42
+ r = @page.add_revision!(params[:data], params[:preview].to_s == 'false', respond_to?(:current_user) ? current_user.id : nil)
43
+ render json: { id: r.id }
44
44
  end
45
45
 
46
46
  def create
@@ -81,7 +81,7 @@ module DocumentationEditor
81
81
  image.caption = params[:caption]
82
82
  image.image = params[:file]
83
83
  image.save!
84
- render json: { id: image.id, url: image.image.url }
84
+ render json: { id: image.id, url: image.image.url, caption: image.caption }
85
85
  end
86
86
 
87
87
  def upload_thumbnail
@@ -1,5 +1,5 @@
1
1
  %div{"ng-app" => "documentationEditorApp"}
2
- %section#documentation-editor{"ng-controller" => 'EditorController', "ng-init" => "init(#{@page.id}, #{@page.slug.to_json}, #{@page.thumbnail_url.to_json}, '#{admin_path.gsub(/\/admin$/, '')}')"}
2
+ %section#documentation-editor{"ng-controller" => 'EditorController', "ng-init" => "init(#{@page.id}, #{@page.slug.to_json}, #{@page.thumbnail_url.to_json}, '#{admin_path.gsub(/\/admin$/, '')}', #{@page.published_revision_id.to_i}, #{@page.revisions.last.id.to_i})"}
3
3
  .editor-header
4
4
  %ul.list-inline.pull-right
5
5
  %li
@@ -7,15 +7,15 @@
7
7
  %i.fa.fa-chevron-left
8
8
  Back to Admin
9
9
  %li
10
- %a.btn.btn-success.btn-sm{href: '#', 'ng-click' => 'save($event)'}
10
+ %a.btn.btn-default.btn-sm{href: '#', 'ng-click' => 'save($event)', 'ng-disabled' => '!diff()'}
11
11
  %i.fa.fa-save
12
12
  Save
13
13
  %li
14
- %a.btn.btn-default.btn-sm{href: '#', 'ng-click' => 'preview($event)'}
14
+ %a.btn.btn-success.btn-sm{href: '#', 'ng-click' => 'preview($event)'}
15
15
  %i.fa.fa-eye
16
16
  Save & Preview
17
17
  %li
18
- %a.btn.btn-danger.btn-sm{href: '#', 'ng-click' => 'publish($event)'}
18
+ %a.btn.btn-danger.btn-sm{href: '#', 'ng-click' => 'publish($event)', 'ng-disabled' => 'published_revision_id === last_revision_id'}
19
19
  %i.fa.fa-save
20
20
  Save & Publish
21
21
  %li
@@ -54,12 +54,16 @@
54
54
  %p
55
55
  Caption:
56
56
  %span.markdown{contenteditable: true, 'ng-model' => 'sections[sectionIndex].content.caption'}
57
- %p
58
- Float:
59
- %select{"ng-model" => "sections[sectionIndex].content.float"}
60
- %option{value: ''} None
61
- %option{value: 'left'} Left
62
- %option{value: 'right'} Right
57
+ %ul.list-inline
58
+ %li
59
+ Float:
60
+ %select{"ng-model" => "sections[sectionIndex].content.float"}
61
+ %option{value: ''} None
62
+ %option{value: 'left'} Left
63
+ %option{value: 'right'} Right
64
+ %li
65
+ Width:
66
+ %input{"ng-model" => "sections[sectionIndex].content.width", type: 'number'}
63
67
  %table.table.parameters{"ng-if" => "section.type === 'parameters'"}
64
68
  %thead
65
69
  %tr
@@ -81,6 +85,17 @@
81
85
  %i.fa.fa-plus-circle
82
86
  %a{href: '#', 'ng-click' => 'deleteRow($event, section.id)'}>
83
87
  %i.fa.fa-minus-circle
88
+ .buttons{"ng-if" => "section.type === 'buttons'"}
89
+ .pull-right
90
+ Button:
91
+ %a{href: '#', 'ng-click' => 'addButton($event, section.id)'}>
92
+ %i.fa.fa-plus-circle
93
+ %ul.list-inline
94
+ %li{"ng-repeat" => "button in section.content.buttons track by $index"}
95
+ %a.btn.btn-default{contenteditable: true, 'ng-model' => 'sections[sectionIndex].content.buttons[$index].label', href: '#'}
96
+ %input{'ng-model' => 'sections[sectionIndex].content.buttons[$index].link'}
97
+ %a{href: '#', 'ng-click' => 'deleteButton($event, section.id, $index)'}>
98
+ %i.fa.fa-remove>
84
99
  .code{"ng-if" => "section.type === 'code'"}
85
100
  .pull-right
86
101
  Language:
@@ -93,6 +108,8 @@
93
108
  %a{contenteditable: true, href: '#snippet_{{section.id}}_{{$index}}', 'data-toggle' => 'tab', 'ng-model' => 'sections[sectionIndex].content.codes[$index].language' }>
94
109
  .tab-content
95
110
  %pre.tab-pane{contenteditable: true, "ng-repeat" => "code in section.content.codes track by $index", "ng-class" => "{ 'in active': ($index === 0) }", id: 'snippet_{{section.id}}_{{$index}}', 'ng-model' => 'sections[sectionIndex].content.codes[$index].code'}
111
+ .html{"ng-if" => "section.type === 'html'"}
112
+ %pre.tab-pane{contenteditable: true, id: 'snippet_{{section.id}}_{{$index}}', 'ng-model' => 'sections[sectionIndex].content.body'}
96
113
 
97
114
  .insert-sections
98
115
  .pull-right
@@ -118,6 +135,9 @@
118
135
  %a.btn.btn-default{href: '#', "data-toggle" => "modal", "data-target" => "#editor-images", "ng-click" => "saveImagePosition(section.id)"}
119
136
  %i.fa.fa-image
120
137
  Add image
138
+ %a.btn.btn-default{href: '#', 'ng-click' => 'addHTML($event, section.id)'}
139
+ %i.fa.fa-code
140
+ Add HTML
121
141
  %a.btn.btn-info{href: '#', 'ng-click' => 'addCallout($event, "info", section.id)'}
122
142
  %i.fa.fa-info-circle
123
143
  Add info
@@ -136,6 +156,9 @@
136
156
  %a.btn.btn-default{href: '#', 'ng-click' => 'addEndIf($event, section.id)'}
137
157
  %i.fa.fa-scissors
138
158
  \/IF
159
+ %a.btn.btn-default{href: '#', 'ng-click' => 'addButtons($event, section.id)'}
160
+ %i.fa.fa-link
161
+ Add buttons
139
162
 
140
163
  #editor-images.modal.fade{"ng-controller" => "ImageUploaderController"}
141
164
  .modal-dialog
@@ -147,19 +170,21 @@
147
170
 
148
171
  .modal-body
149
172
  - if DocumentationEditor::Image.count > 0
150
- %h3 Existing images:
173
+ %h5 Choose an existing image:
151
174
  .images
152
175
  - DocumentationEditor::Image.limit(100).order('id DESC').each do |image|
153
- %a{href: '#', 'ng-click' => "addImage('#{image.image.url}')"}= image_tag image.image.url
176
+ %a{href: '#', 'ng-click' => "addImage('#{image.image.url}', '#{escape_javascript image.caption}')"}= image_tag image.image.url
177
+ %hr
178
+ %h4.text-center Or
154
179
  %hr
155
- %h3 Upload a new image
180
+ %h5 Upload a new one:
156
181
  .form
157
182
  .row.form-group
158
183
  .col-sm-3.text-right Caption
159
184
  .col-sm-9
160
185
  %input.form-control{type: 'text', 'ng-model' => 'caption', placeholder: 'Optional caption'}
161
186
  .text-center.form-group
162
- .btn.btn-success{"ngf-select" => true, "ng-model" => 'files'} Select image
187
+ .btn.btn-success{"ngf-select" => true, "ng-model" => 'files'} Browse & upload image
163
188
 
164
189
  #upload-thumbnail.modal.fade{"ng-controller" => "ThumbnailUploaderController"}
165
190
  .modal-dialog
@@ -8,15 +8,27 @@
8
8
  %a.btn.btn-default.btn-sm{href: admin_path}
9
9
  %i.fa.fa-chevron-left
10
10
  Back to Admin
11
+ %li
12
+ %a.btn.btn-default.btn-sm{href: edit_page_path(@page)}
13
+ %i.fa.fa-edit
14
+ Edit
11
15
  %h1
12
16
  History of
13
17
  %small
14
18
  = page_path('')
15
19
  = @page.slug
16
20
  .editor-content
21
+ - if !@page.published_revision
22
+ .alert.alert-danger
23
+ %i.fa.fa-exclamation-triangle
24
+ This page has never been published
25
+ - elsif @page.revisions.last != @page.published_revision
26
+ .alert.alert-warning
27
+ %i.fa.fa-exclamation-triangle
28
+ The latest version of this page has not been published yet.
17
29
  .row{"ng-controller" => 'HistoryController', "ng-init" => "init(#{@page.id}, '#{admin_path.gsub(/\/admin$/, '')}')"}
18
30
  .col-sm-3
19
- %table.table.table-striped
31
+ %table.table.table-striped.history
20
32
  %thead
21
33
  %tr
22
34
  %th.text-right Revision
@@ -24,7 +36,7 @@
24
36
  %th
25
37
  %th Modification date
26
38
  %tbody
27
- %tr{"ng-repeat" => "revision in revisions track by revision.id"}
39
+ %tr{"ng-repeat" => "revision in revisions track by revision.id", 'ng-class' => "{published: (#{@page.published_revision_id.to_i} == revision.id)}"}
28
40
  %td.text-right {{ revisions.length - $index }}
29
41
  %td
30
42
  %input{type: 'radio', name: 'prev', "ng-value" => "$index", "ng-model" => "diff.prev", "ng-if" => "$index > diff.cur"}
@@ -12,6 +12,8 @@ require 'paperclip'
12
12
  require 'diffy'
13
13
  ## best in place
14
14
  require 'best_in_place'
15
+ ## htmlentities
16
+ require 'htmlentities'
15
17
 
16
18
  module DocumentationEditor
17
19
  end
@@ -1,6 +1,7 @@
1
1
  require 'kramdown/document'
2
2
  require 'kramdown/parser/kramdown'
3
3
  require 'simplabs/highlight'
4
+ require 'htmlentities'
4
5
 
5
6
  class Kramdown::Parser::BlockKramdown < Kramdown::Parser::Kramdown
6
7
 
@@ -45,6 +46,9 @@ class Kramdown::Parser::BlockKramdown < Kramdown::Parser::Kramdown
45
46
  callout = new_block_el(:html_element, 'div', { class: "alert alert-#{content['type']}" })
46
47
  callout.children << Element.new(:raw, parse_cached(content['body']))
47
48
  @tree.children << callout
49
+ when 'html'
50
+ coder = HTMLEntities.new
51
+ @tree.children << Element.new(:raw, coder.decode(content['body']))
48
52
  when 'image'
49
53
  clazz = case content['float']
50
54
  when 'left'
@@ -54,12 +58,23 @@ class Kramdown::Parser::BlockKramdown < Kramdown::Parser::Kramdown
54
58
  else
55
59
  nil
56
60
  end
61
+ attrs = { src: content['images'][0]['image'][0] }
62
+ if !content['width'].blank?
63
+ attrs[:width] = content['width'].to_i
64
+ end
57
65
  @tree.children << Element.new(:html_element, 'figure', { class: clazz })
58
- @tree.children.last.children << Element.new(:img, nil, { src: content['images'][0]['image'][0] })
66
+ @tree.children.last.children << Element.new(:img, nil, attrs)
59
67
  unless content['caption'].blank?
60
68
  @tree.children.last.children << Element.new(:html_element, 'figcaption')
61
69
  @tree.children.last.children.last.children << Element.new(:raw, parse_cached(content['caption']))
62
70
  end
71
+ when 'buttons'
72
+ buttons = new_block_el(:html_element, 'div', { class: "text-center" })
73
+ content['buttons'].each do |b|
74
+ buttons.children << Element.new(:html_element, 'a', { href: b['link'], class: 'btn btn-default' })
75
+ buttons.children.last.children << Element.new(:raw, b['label'])
76
+ end
77
+ @tree.children << buttons
63
78
  when 'if'
64
79
  @tree.children << Element.new(:comment, "if #{content['condition']}", { }, { start: true, condition: content['condition'], negation: false })
65
80
  when 'ifnot'
@@ -127,7 +142,7 @@ class Kramdown::Parser::BlockKramdown < Kramdown::Parser::Kramdown
127
142
  language
128
143
  end
129
144
  cache "#{language}_#{code.hash}" do
130
- Simplabs::Highlight.highlight(language, code)
145
+ Simplabs::Highlight.highlight(language, CGI.unescapeHTML(code))
131
146
  end
132
147
  end
133
148
 
@@ -1,3 +1,3 @@
1
1
  module DocumentationEditor
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: documentation-editor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Algolia Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-30 00:00:00.000000000 Z
11
+ date: 2015-11-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: htmlentities
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  description: The goal of this project is to provide an easy & frictionless way to
126
140
  edit an online tech documentation. The sweet spot of this editor is to be able to
127
141
  generate pages containing multiple snippets of highlighted code & conditional sections
@@ -186,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
200
  version: '0'
187
201
  requirements: []
188
202
  rubyforge_project:
189
- rubygems_version: 2.4.8
203
+ rubygems_version: 2.2.2
190
204
  signing_key:
191
205
  specification_version: 4
192
206
  summary: Mountable Rails application providing an extended Markdown documentation