diff --git a/Gemfile b/Gemfile index 50afc353..b2d66d8f 100644 --- a/Gemfile +++ b/Gemfile @@ -81,6 +81,9 @@ gem 'active_model_serializers' # Use FontAwesome for splash themes gem 'font-awesome-rails' +#Use Redcarpet for Markdown in description +gem 'redcarpet' + # Use rspec and capybara as testing framework group :development, :test do gem 'rspec', '>= 3.0.0.beta' diff --git a/Gemfile.lock b/Gemfile.lock index 201fc01b..4a8f57d1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -273,6 +273,7 @@ GEM loggability (~> 0.6) rdoc (~> 4.0) yajl-ruby (~> 1.1) + redcarpet (3.1.2) referer-parser (0.2.1) request_store (1.0.6) rest-client (1.6.7) @@ -413,6 +414,7 @@ DEPENDENCIES rails (~> 4.1) rails-observers rdoc-generator-fivefish + redcarpet rspec (>= 3.0.0.beta) rspec-rails (>= 3.0.0.beta) rubocop diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 38348be6..0898e46b 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -24,7 +24,9 @@ //= require dashboard //= require ahoy //= require smoothscroll - +//= require bootstrap-markdown +//= require to-markdown +//= require markdown $(document).ready(function() { $('a[disabled=disabled]').click(function(event){ return false; diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index febb4d2e..a1cf9dbc 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -10,4 +10,5 @@ *= require osem-schedule *= require osem-schedule-print *= require font-awesome + *= require bootstrap-markdown.min */ diff --git a/app/assets/stylesheets/bootstrap-markdown.min.css b/app/assets/stylesheets/bootstrap-markdown.min.css new file mode 100644 index 00000000..ac71e895 --- /dev/null +++ b/app/assets/stylesheets/bootstrap-markdown.min.css @@ -0,0 +1 @@ +.md-editor{display:block;border:1px solid #ddd}.md-editor>.md-header,.md-editor .md-footer{display:block;padding:6px 4px;background:#fff}.md-editor>.md-header{margin: 0;}.md-editor>.md-preview{background:#fff;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd;min-height:10px;overflow:auto}.md-editor>textarea{font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:14px;outline:0;outline:thin dotted \9;margin:0;display:block;padding:0;width:100%;border:0;border-top:1px dashed #ddd;border-bottom:1px dashed #ddd;border-radius:0;box-shadow:none;background:#eee}.md-editor>textarea:focus{box-shadow:none;background:#fff}.md-editor.active{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,0.6)} \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 6849105d..62a97153 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -197,4 +197,18 @@ module ApplicationHelper (short_title_brand(@conference) if @conference) || default_brand end + + def markdown(text) + options = { + autolink: true, + space_after_headers: true, + no_intra_emphasis: true + } + markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, options) + markdown.render(text).html_safe + end + + def markdown_hint(text="") + markdown("#{text} Please look at #{link_to '**Markdown Syntax**', 'https://daringfireball.net/projects/markdown/syntax', target: '_blank'} to format your text") + end end diff --git a/app/views/admin/callforpapers/show.html.haml b/app/views/admin/callforpapers/show.html.haml index 64c42606..8b76117a 100644 --- a/app/views/admin/callforpapers/show.html.haml +++ b/app/views/admin/callforpapers/show.html.haml @@ -4,7 +4,7 @@ = f.input :include_cfp_in_splash, label: 'Include Call for Papers in Splash', hint: 'On setting this true you will enable the call for papers to be displayed on the splash page' = f.input :start_date, :as => :string, :input_html => { :id => "conference-start-datepicker", :readonly => "readonly" } = f.input :end_date, :as => :string, :input_html => { :id => "conference-end-datepicker", :readonly => "readonly" } - = f.input :description, :hint => "Welcome text for the Call for Papers" + = f.input :description, hint: markdown_hint("Welcome text for the Call for Papers."), input_html: { data: { provide: "markdown-editable" } } = f.input :schedule_changes = f.input :schedule_public = f.input :rating, :hint => "Enter the number of different rating levels you want to have for voting on proposals. Enter 0 if you do not want to vote on proposals." diff --git a/app/views/admin/conference/edit.html.haml b/app/views/admin/conference/edit.html.haml index c2725053..cf5eeee8 100644 --- a/app/views/admin/conference/edit.html.haml +++ b/app/views/admin/conference/edit.html.haml @@ -12,7 +12,7 @@ = f.inputs name: 'Banner for Splash' do = f.input :include_banner_in_splash, hint: 'This enable the Banner Photo with description to be displayed on the splash' = f.input :banner_photo, hint: 'This will be the top most component of the splash.This background cover will contain start_date and end_date of the conference and the description' - = f.input :description, hint: 'This description will be shown at the bottom of the banner photo of the Splash' + = f.input :description, hint: markdown_hint("This description will be shown at the bottom of the banner photo of the Splash."), input_html: { data: { provide: "markdown-editable" } } = f.inputs :name => "Scheduling" do = f.input :timezone, :as => :time_zone, :hint => "The conference time zone" @@ -22,11 +22,11 @@ = f.input :include_registrations_in_splash, hint: 'On setting this true you will enable the registrations to be displayed on the splash page' = f.input :registration_start_date, :as => :string, :input_html => { :id => "conference-reg-start-datepicker", :readonly => "readonly" } = f.input :registration_end_date, :as => :string, :input_html => { :id => "conference-reg-end-datepicker", :readonly => "readonly" } - = f.input :registration_description, hint: 'This description will appear in registration segment of the splash' - = f.input :ticket_description, hint: 'This will appear in the Tickets segment of the splash' - = f.input :sponsor_description, hint: 'This will appear in the sponsor segment of the splash' + = f.input :registration_description, hint: markdown_hint("This description will appear in registration segment of the splash."), input_html: { data: { provide: "markdown-editable" } } + = f.input :ticket_description, hint: markdown_hint("This will appear in the Tickets segment of the splash."), input_html: { data: { provide: "markdown-editable" } } + = f.input :sponsor_description, hint: markdown_hint("This will appear in the sponsor segment of the splash."), input_html: { data: { provide: "markdown-editable" } } = f.input :sponsor_email, hint: 'This will appear in the sponsor segment of the splash for the sponsors to contact to the organizers' - = f.input :lodging_description, hint: 'This will appear in the lodging segment of the splash' + = f.input :lodging_description, hint: markdown_hint("This will appear in the lodging segment of the splash."), input_html: { data: { provide: "markdown-editable" } } = dynamic_association :photos, "Photos", f diff --git a/app/views/admin/lodgings/_lodging_fields.html.erb b/app/views/admin/lodgings/_lodging_fields.html.erb index 83c27724..11e74a18 100644 --- a/app/views/admin/lodgings/_lodging_fields.html.erb +++ b/app/views/admin/lodgings/_lodging_fields.html.erb @@ -1,8 +1,7 @@
<%= f.inputs do %> <%= f.input :name %> - <%= f.input :description, :input_html => {:rows => 2 }, hint: 'Write about the hotel/place, - of what they are offering, about types of rooms, prices and address' %> + <%= f.input :description, :input_html => {:rows => 2, data: { provide: "markdown-editable" } }, hint: markdown_hint("Write about the hotel/place, of what they are offering, about types of rooms, prices and address.") %> <%= image_tag f.object.photo(:thumb) if !f.object.photo.blank? %> <%= f.input :photo %> <%= f.input :website_link %> diff --git a/app/views/admin/supporter_levels/_supporter_level_fields.html.erb b/app/views/admin/supporter_levels/_supporter_level_fields.html.erb index 899efdac..7b6621a9 100644 --- a/app/views/admin/supporter_levels/_supporter_level_fields.html.erb +++ b/app/views/admin/supporter_levels/_supporter_level_fields.html.erb @@ -2,7 +2,7 @@ <%= f.inputs do %> <%= f.input :title%> <%= f.input :url %> - <%= f.input :description %> + <%= f.input :description, hint: markdown_hint, input_html: { data: { provide: "markdown-editable" } } %> <%= f.input :ticket_price, hint: 'Please enter price with currency symbol. For example, $200,₹300' %> <%= remove_association_link :supporter_level, f %> diff --git a/app/views/admin/tracks/_track_fields.html.erb b/app/views/admin/tracks/_track_fields.html.erb index 40d21a29..78463fd5 100644 --- a/app/views/admin/tracks/_track_fields.html.erb +++ b/app/views/admin/tracks/_track_fields.html.erb @@ -2,7 +2,8 @@ <%= f.inputs do %> <%= f.input :name %> <%= f.input :color, :input_html => {:size => 6, :type => "color"}, :required=> true %> - <%= f.input :description, :input_html => {:rows => 2 } %> + <%= f.input :description, :input_html => {:rows => 2, data: { provide: "markdown-editable" } }, hint: markdown_hint %> <%= remove_association_link :track, f %> <% end %>
+data: { provide: "markdown-editable" } diff --git a/app/views/admin/venue/venue_info.html.haml b/app/views/admin/venue/venue_info.html.haml index bf437061..f747bad6 100644 --- a/app/views/admin/venue/venue_info.html.haml +++ b/app/views/admin/venue/venue_info.html.haml @@ -5,7 +5,7 @@ = f.input :name, :input_html => {:rows => 1} = f.input :address, :input_html => {:rows => 1} = f.input :website - = f.input :description, :input_html => { :rows => 10, :cols => 20 } + = f.input :description, :input_html => { :rows => 10, :cols => 20, data: { provide: "markdown-editable" } }, hint: markdown_hint - unless @venue.photo.blank? = image_tag @venue.photo(:thumb) = f.input :photo diff --git a/app/views/conference/_call_for_papers.html.haml b/app/views/conference/_call_for_papers.html.haml index 4d453980..f669c388 100644 --- a/app/views/conference/_call_for_papers.html.haml +++ b/app/views/conference/_call_for_papers.html.haml @@ -9,7 +9,7 @@ Call For Papers - if !@conference.call_for_papers.description.blank? %p - = @conference.call_for_papers.description + = markdown(@conference.call_for_papers.description) - if !@conference.call_for_papers.start_date.blank? && !@conference.call_for_papers.end_date.blank? %p.lead Proposals will be accepted between #cfp-date diff --git a/app/views/conference/_location.html.haml b/app/views/conference/_location.html.haml index e6d4c62f..c41a1ed9 100644 --- a/app/views/conference/_location.html.haml +++ b/app/views/conference/_location.html.haml @@ -14,7 +14,7 @@ %strong #{ @conference.venue.address } - unless @conference.venue.description.blank? %p - = @conference.venue.description + = markdown(@conference.venue.description) %ul.location-links - unless @conference.venue.address.blank? %li diff --git a/app/views/conference/_lodging.html.haml b/app/views/conference/_lodging.html.haml index f9b4296c..43864ba4 100644 --- a/app/views/conference/_lodging.html.haml +++ b/app/views/conference/_lodging.html.haml @@ -2,7 +2,7 @@ %h2 Lodgings -unless @conference.lodging_description.blank? %p.lead - = @conference.lodging_description + = markdown(@conference.lodging_description) %div.row.text-center - @conference.venue.lodgings.each do |r| @@ -13,7 +13,7 @@ -unless r.name.blank? %h4.text-center #{ r.name } -unless r.description.blank? - %p.text-left #{ r.description } + %p.text-left #{ markdown(r.description) } - unless r.website_link.blank? .text-center = link_to 'Go to Website', r.website_link diff --git a/app/views/conference/_registration.html.haml b/app/views/conference/_registration.html.haml index 654bb2ca..124d3209 100644 --- a/app/views/conference/_registration.html.haml +++ b/app/views/conference/_registration.html.haml @@ -6,7 +6,7 @@ %h1 Registration - if !@conference.registration_description.blank? %p - = @conference.registration_description + = markdown(@conference.registration_description) - if @conference.registration_dates_given? -if @conference.registration_end_date >= Date.today %h4 Registration period #{ date_string(@conference.registration_start_date, @conference.registration_end_date) } diff --git a/app/views/conference/_sponsor.html.haml b/app/views/conference/_sponsor.html.haml index 83dde725..9480fd04 100644 --- a/app/views/conference/_sponsor.html.haml +++ b/app/views/conference/_sponsor.html.haml @@ -6,7 +6,8 @@ %div.col-md-12 %h2 Sponsors -if !@conference.sponsor_description.blank? - %p #{ @conference.sponsor_description } + %p + = markdown(@conference.sponsor_description) -if !@conference.sponsor_email.blank? %h3= mail_to "#{ @conference.sponsor_email }", 'Become a Sponsor' - if !@conference.sponsorship_levels.blank? diff --git a/app/views/conference/_tickets.html.haml b/app/views/conference/_tickets.html.haml index 2fcfbf72..1387f94b 100644 --- a/app/views/conference/_tickets.html.haml +++ b/app/views/conference/_tickets.html.haml @@ -1,7 +1,8 @@ %div.row %h3 Tickets -if !@conference.ticket_description.blank? - %p #{ @conference.ticket_description } + %p + = markdown(@conference.ticket_description) - @conference.supporter_levels.each do |s| %div.col-md-6 - if !s.title.blank? diff --git a/app/views/conference/_tracks.html.haml b/app/views/conference/_tracks.html.haml index f3c85c87..09758ea1 100644 --- a/app/views/conference/_tracks.html.haml +++ b/app/views/conference/_tracks.html.haml @@ -6,4 +6,4 @@ %h4 = t.name %p - = t.description + = markdown(t.description) diff --git a/app/views/conference/show.html.haml b/app/views/conference/show.html.haml index be839e6b..43e67fb3 100644 --- a/app/views/conference/show.html.haml +++ b/app/views/conference/show.html.haml @@ -16,7 +16,8 @@ - unless @conference.description.blank? .row-fluid .col-md-12.lead - %p= @conference.description + %p + = markdown(@conference.description) -unless @conference.photos.blank? = link_to 'Gallery', gallery_photos_conference_path(@conference.short_title), class: 'btn btn-primary btn-lg', id: 'gallery-btn', remote: true = render 'gallery' diff --git a/vendor/assets/javascripts/bootstrap-markdown.js b/vendor/assets/javascripts/bootstrap-markdown.js new file mode 100644 index 00000000..2256df64 --- /dev/null +++ b/vendor/assets/javascripts/bootstrap-markdown.js @@ -0,0 +1,1293 @@ +/* =================================================== + * bootstrap-markdown.js v2.5.0 + * http://github.com/toopay/bootstrap-markdown + * =================================================== + * Copyright 2013 Taufan Aditya + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + +!function ($) { + + "use strict"; // jshint ;_; + + + /* MARKDOWN CLASS DEFINITION + * ========================== */ + + var Markdown = function (element, options) { + // Class Properties + this.$ns = 'bootstrap-markdown' + this.$element = $(element) + this.$editable = {el:null, type:null,attrKeys:[], attrValues:[], content:null} + this.$options = $.extend(true, {}, $.fn.markdown.defaults, options, this.$element.data(), this.$element.data('options')) + this.$oldContent = null + this.$isPreview = false + this.$editor = null + this.$textarea = null + this.$handler = [] + this.$callback = [] + this.$nextTab = [] + + this.showEditor() + } + + Markdown.prototype = { + + constructor: Markdown + + , __alterButtons: function(name,alter) { + var handler = this.$handler, isAll = (name == 'all'),that = this + + $.each(handler,function(k,v) { + var halt = true + if (isAll) { + halt = false + } else { + halt = v.indexOf(name) < 0 + } + + if (halt == false) { + alter(that.$editor.find('button[data-handler="'+v+'"]')) + } + }) + } + + , __buildButtons: function(buttonsArray, container) { + var i, + ns = this.$ns, + handler = this.$handler, + callback = this.$callback + + for (i=0;i', { + 'class': 'btn-group' + }) + + for (z=0;z ' + +this.__localize(btnText) + +'') + + // Register handler and callback + handler.push(buttonHandler) + callback.push(button.callback) + } + + // Attach the button group into container dom + container.append(btnGroupContainer) + } + } + + return container + } + , __setListener: function() { + // Set size and resizable Properties + var hasRows = typeof this.$textarea.attr('rows') != 'undefined', + maxRows = this.$textarea.val().split("\n").length > 5 ? this.$textarea.val().split("\n").length : '5', + rowsVal = hasRows ? this.$textarea.attr('rows') : maxRows + + this.$textarea.attr('rows',rowsVal) + if (this.$options.resize) { + this.$textarea.css('resize',this.$options.resize) + } + + this.$textarea + .on('focus', $.proxy(this.focus, this)) + .on('keypress', $.proxy(this.keypress, this)) + .on('keyup', $.proxy(this.keyup, this)) + .on('change', $.proxy(this.change, this)) + + if (this.eventSupported('keydown')) { + this.$textarea.on('keydown', $.proxy(this.keydown, this)) + } + + // Re-attach markdown data + this.$textarea.data('markdown',this) + } + + , __handle: function(e) { + var target = $(e.currentTarget), + handler = this.$handler, + callback = this.$callback, + handlerName = target.attr('data-handler'), + callbackIndex = handler.indexOf(handlerName), + callbackHandler = callback[callbackIndex] + + // Trigger the focusin + $(e.currentTarget).focus() + + callbackHandler(this) + + // Trigger onChange for each button handle + this.change(this); + + // Unless it was the save handler, + // focusin the textarea + if (handlerName.indexOf('cmdSave') < 0) { + this.$textarea.focus() + } + + e.preventDefault() + } + + , __localize: function(string) { + var messages = $.fn.markdown.messages, + language = this.$options.language + if ( + typeof messages !== 'undefined' && + typeof messages[language] !== 'undefined' && + typeof messages[language][string] !== 'undefined' + ) { + return messages[language][string]; + } + return string; + } + + , showEditor: function() { + var instance = this, + textarea, + ns = this.$ns, + container = this.$element, + originalHeigth = container.css('height'), + originalWidth = container.css('width'), + editable = this.$editable, + handler = this.$handler, + callback = this.$callback, + options = this.$options, + editor = $( '
', { + 'class': 'md-editor', + click: function() { + instance.focus() + } + }) + + // Prepare the editor + if (this.$editor == null) { + // Create the panel + var editorHeader = $('
', { + 'class': 'md-header btn-toolbar' + }) + + // Merge the main & additional button groups together + var allBtnGroups = [] + if (options.buttons.length > 0) allBtnGroups = allBtnGroups.concat(options.buttons[0]) + if (options.additionalButtons.length > 0) allBtnGroups = allBtnGroups.concat(options.additionalButtons[0]) + + // Reduce and/or reorder the button groups + if (options.reorderButtonGroups.length > 0) { + allBtnGroups = allBtnGroups + .filter(function(btnGroup) { + return options.reorderButtonGroups.indexOf(btnGroup.name) > -1 + }) + .sort(function(a, b) { + if (options.reorderButtonGroups.indexOf(a.name) < options.reorderButtonGroups.indexOf(b.name)) return -1 + if (options.reorderButtonGroups.indexOf(a.name) > options.reorderButtonGroups.indexOf(b.name)) return 1 + return 0 + }) + } + + // Build the buttons + if (allBtnGroups.length > 0) { + editorHeader = this.__buildButtons([allBtnGroups], editorHeader) + } + + editor.append(editorHeader) + + // Wrap the textarea + if (container.is('textarea')) { + container.before(editor) + textarea = container + textarea.addClass('md-input') + editor.append(textarea) + } else { + var rawContent = (typeof toMarkdown == 'function') ? toMarkdown(container.html()) : container.html(), + currentContent = $.trim(rawContent) + + // This is some arbitrary content that could be edited + textarea = $('